Source code for taurus.qt.qtgui.model.qbasemodel
#!/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/>.
##
#############################################################################
"""This module provides base widget that displays a Qt view widget envolved
by a toolbar and a status bar (optional)"""
__all__ = ["QBaseModelWidget", "TaurusBaseModelWidget",
"BaseToolBar", "FilterToolBar", "EditorToolBar", "SelectionToolBar",
"RefreshToolBar", "PerspectiveToolBar"]
__docformat__ = 'restructuredtext'
from taurus.external.qt import Qt
from taurus.qt.qtgui.util import ActionFactory
from taurus.qt.qtgui.base import TaurusBaseWidget
[docs]class BaseToolBar(Qt.QToolBar):
def __init__(self, name=None, view=None, parent=None, designMode=False):
if name is None:
name = "Base toolbar"
self._viewWidget = view or parent
Qt.QToolBar.__init__(self, name, parent)
self.setIconSize(Qt.QSize(16, 16))
self.setFloatable(False)
self.setMovable(False)
[docs]class FilterToolBar(BaseToolBar):
"""Internal widget providing quick filter to be placed in a _QToolArea"""
filterEdited = Qt.pyqtSignal('const QString &')
filterChanged = Qt.pyqtSignal('const QString &')
clearFilterTriggered = Qt.pyqtSignal()
def __init__(self, view=None, parent=None, designMode=False):
BaseToolBar.__init__(self, name="Taurus filter toolbar", view=view,
parent=parent, designMode=designMode)
filterLineEdit = self._filterLineEdit = Qt.QLineEdit(self)
filterLineEdit.setSizePolicy(Qt.QSizePolicy(Qt.QSizePolicy.Preferred,
Qt.QSizePolicy.Preferred))
filterLineEdit.setToolTip("Quick filter")
filterLineEdit.textChanged.connect(self.onFilterChanged)
filterLineEdit.textEdited.connect(self.onFilterEdited)
self.addWidget(filterLineEdit)
af = ActionFactory()
self._clearFilterAction = af.createAction(self, "Clear",
icon=Qt.QIcon.fromTheme(
"edit-clear"),
tip="Clears the filter",
triggered=self.onClearFilter)
self.addAction(self._clearFilterAction)
[docs] def onClearFilter(self):
self.getFilterLineEdit().setText("")
self.clearFilterTriggered.emit()
[docs] def onFilterChanged(self, text=None):
text = text or self.getFilterLineEdit().text()
self.filterChanged.emit(text)
[docs] def onFilterEdited(self, text=None):
text = text or self.getFilterLineEdit().text()
self.filterEdited.emit(text)
[docs]class EditorToolBar(BaseToolBar):
"""Internal widget to be placed in a _QToolArea providing buttons for
moving, adding and removing items from a view based widget"""
addTriggered = Qt.pyqtSignal()
removeTriggered = Qt.pyqtSignal()
moveTopTriggered = Qt.pyqtSignal()
moveDownTriggered = Qt.pyqtSignal()
moveUpTriggered = Qt.pyqtSignal()
moveBottomTriggered = Qt.pyqtSignal()
def __init__(self, view=None, parent=None, designMode=False):
BaseToolBar.__init__(self, name="Taurus editor toolbar", view=view,
parent=parent, designMode=designMode)
af = ActionFactory()
self._addAction = af.createAction(self, "New item",
icon=Qt.QIcon.fromTheme("list-add"),
tip="Add new item",
triggered=self.onAdd)
self._removeAction = af.createAction(self, "Remove item",
icon=Qt.QIcon.fromTheme(
"list-remove"),
tip="Remove item",
triggered=self.onRemove)
self._moveTopAction = af.createAction(self, "To top",
icon=Qt.QIcon.fromTheme("go-top"),
tip="Move selected item to top",
triggered=self.onMoveTop)
self._moveUpAction = af.createAction(self, "Move up",
icon=Qt.QIcon.fromTheme("go-up"),
tip="Move selected item up one level",
triggered=self.onMoveUp)
self._moveDownAction = af.createAction(self, "Move down",
icon=Qt.QIcon.fromTheme(
"go-down"),
tip="Move selected item down one level",
triggered=self.onMoveDown)
self._moveBottomAction = af.createAction(self, "To bottom",
icon=Qt.QIcon.fromTheme(
"go-bottom"),
tip="Move selected item to bottom",
triggered=self.onMoveBottom)
self.addAction(self._addAction)
self.addAction(self._removeAction)
self.addAction(self._moveTopAction)
self.addAction(self._moveUpAction)
self.addAction(self._moveDownAction)
self.addAction(self._moveBottomAction)
#self.setStyleSheet("QWidget {background : red; }")
[docs]class SelectionToolBar(BaseToolBar):
selectAllTriggered = Qt.pyqtSignal()
clearSelectionTriggered = Qt.pyqtSignal()
def __init__(self, view=None, parent=None, designMode=False):
BaseToolBar.__init__(self, name="Taurus selection toolbar", view=view,
parent=parent, designMode=designMode)
af = ActionFactory()
self._selectAllAction = af.createAction(self, "Select All",
icon=Qt.QIcon.fromTheme(
"edit-select-all"),
tip="Select all items",
triggered=self.onSelectAll)
self._clearSelectionAction = af.createAction(self, "Clear selection",
icon=Qt.QIcon.fromTheme(
"edit-clear"),
tip="Clears current selection",
triggered=self.onclearSelection)
self.addAction(self._selectAllAction)
self.addAction(self._clearSelectionAction)
[docs]class RefreshToolBar(BaseToolBar):
refreshTriggered = Qt.pyqtSignal()
def __init__(self, view=None, parent=None, designMode=False):
BaseToolBar.__init__(self, name="Taurus refresh toolbar", view=view,
parent=parent, designMode=designMode)
af = ActionFactory()
self._refreshAction = af.createAction(self, "Refresh",
icon=Qt.QIcon.fromTheme(
"view-refresh"),
tip="Refresh view",
triggered=self.onRefresh)
self.addAction(self._refreshAction)
[docs]class PerspectiveToolBar(BaseToolBar):
perspectiveChanged = Qt.pyqtSignal(str)
def __init__(self, perspective, view=None, parent=None, designMode=False):
BaseToolBar.__init__(self, name="Taurus refresh toolbar", view=view,
parent=parent, designMode=designMode)
self._perspective = perspective
view = self.viewWidget()
b = self._perspective_button = Qt.QToolButton(self)
b.setToolTip("Perspective selection")
b.setPopupMode(Qt.QToolButton.InstantPopup)
b.setToolButtonStyle(Qt.Qt.ToolButtonTextBesideIcon)
menu = Qt.QMenu("Perspective", b)
b.setMenu(menu)
af = ActionFactory()
for persp, persp_data in view.KnownPerspectives.items():
label = persp_data["label"]
icon = Qt.QIcon.fromTheme(persp_data["icon"])
tip = persp_data["tooltip"]
action = af.createAction(self, label, icon=icon, tip=tip,
triggered=self.onSwitchPerspective)
action.perspective = persp
menu.addAction(action)
if persp == perspective:
b.setDefaultAction(action)
self._perspectiveAction = self.addWidget(b)
[docs] def switchPerspectiveButton(self):
"""Returns the QToolButton that handles the switch perspective.
:return: (PyQt4.QtGui.QToolButton) the switch perspective tool button
"""
return self._perspective_button
[docs] def onSwitchPerspective(self):
action = self.sender()
self._perspective = action.perspective
self._perspective_button.setDefaultAction(action)
self.perspectiveChanged.emit(action.perspective)
[docs]class QBaseModelWidget(Qt.QMainWindow):
"""A pure Qt widget designed to display a Qt view widget (QTreeView for
example), envolved by optional toolbar and statusbar.
The Qt model associated with the internal Qt view widget should be a
:class:`taurus.qt.qtcore.model.TaurusBaseModel`"""
KnownPerspectives = {}
DftPerspective = None
itemClicked = Qt.pyqtSignal(object, int)
itemDoubleClicked = Qt.pyqtSignal(object, int)
itemSelectionChanged = Qt.pyqtSignal()
currentItemChanged = Qt.pyqtSignal(object, object)
def __init__(self, parent=None, designMode=False, with_filter_widget=True,
with_selection_widget=True, with_refresh_widget=True,
perspective=None, proxy=None):
Qt.QMainWindow.__init__(self, parent)
self.setWindowFlags(Qt.Qt.Widget)
self._baseQModel = None
self._toolBars = []
if with_filter_widget:
if isinstance(with_filter_widget, (bool, int)):
self._with_filter_widget = FilterToolBar
else:
self._with_filter_widget = with_filter_widget
else:
self._with_filter_widget = None
if with_selection_widget:
if isinstance(with_selection_widget, (bool, int)):
self._with_selection_widget = SelectionToolBar
else:
self._with_selection_widget = with_selection_widget
else:
self._with_selection_widget = None
if with_refresh_widget:
if isinstance(with_refresh_widget, (bool, int)):
self._with_refresh_widget = RefreshToolBar
else:
self._with_refresh_widget = with_refresh_widget
else:
self._with_refresh_widget = None
self._proxyModel = proxy
toolBars = self.createToolArea()
self._viewWidget = self.createViewWidget()
statusbar = self.createStatusBar()
for toolBar in toolBars:
# toolBar.addSeparator()
self.addToolBar(toolBar)
self.setContentsMargins(0, 0, 0, 0)
self.setCentralWidget(self._viewWidget)
self.setStatusBar(statusbar)
if perspective is None:
perspective = self.DftPerspective
if len(self.KnownPerspectives) > 1:
p_bar = self._perspectiveBar = PerspectiveToolBar(
perspective, view=self, parent=self)
p_bar.perspectiveChanged.connect(self.onSwitchPerspective)
self.addToolBar(p_bar)
else:
self._perspectiveBar = None
self._setPerspective(perspective)
[docs] def createToolArea(self):
tb = [] # tb = self._toolArea = QToolArea(self)
if self._with_filter_widget:
f_bar = self._filterBar = self._with_filter_widget(
view=self, parent=self)
f_bar.filterChanged.connect(self.onFilterChanged)
tb.append(f_bar)
else:
self._filterBar = None
if self._with_selection_widget:
s_bar = self._selectionBar = self._with_selection_widget(
view=self, parent=self)
s_bar.selectAllTriggered.connect(self.onSelectAll)
s_bar.clearSelectionTriggered.connect(self.onClearSelection)
tb.append(s_bar)
else:
self._selectionBar = None
if self._with_refresh_widget:
r_bar = self._refreshBar = self._with_refresh_widget(
view=self, parent=self)
r_bar.refreshTriggered.connect(self.onRefreshModel)
tb.append(r_bar)
else:
self._refreshBar = None
return tb
def _onClicked(self, index):
'''Emits an "itemClicked" signal with with the clicked item and column
as arguments'''
item = self._mapToSource(index).internalPointer()
self.itemClicked.emit(item, index.column())
def _onDoubleClicked(self, index):
'''Emits an "itemDoubleClicked" signal with the clicked item and column
as arguments'''
item = self._mapToSource(index).internalPointer()
self.itemDoubleClicked.emit(item, index.column())
def _mapToSource(self, index):
if not self.usesProxyQModel():
return index
model = self.getQModel()
while isinstance(model, Qt.QAbstractProxyModel):
index = model.mapToSource(index)
model = model.sourceModel()
return index
[docs] def setQModel(self, qmodel):
self._baseQModel = qmodel
while isinstance(self._baseQModel, Qt.QAbstractProxyModel):
self._baseQModel = self._baseQModel.sourceModel()
view = self.viewWidget()
old_selection_model = view.selectionModel()
CC = 'currentChanged(const QModelIndex &,const QModelIndex &)'
SC = 'selectionChanged(QItemSelection &, QItemSelection &)'
if old_selection_model is not None:
old_selection_model.currentChanged.disconnect(
self.viewCurrentIndexChanged)
old_selection_model.selectionChanged.disconnect(
self.viewSelectionChanged)
view.setModel(qmodel)
new_selection_model = view.selectionModel()
if new_selection_model is not None:
new_selection_model.currentChanged.connect(
self.viewCurrentIndexChanged)
new_selection_model.selectionChanged.connect(
self.viewSelectionChanged)
view.setCurrentIndex(view.rootIndex())
self._updateToolBar()
[docs] def viewCurrentIndexChanged(self, current, previous):
# if there is a proxy model we have to translate the selection
base_current = self._mapToSource(current)
base_previous = self._mapToSource(previous)
self._updateToolBar(current)
if base_current.isValid():
currentTaurusTreeItem = base_current.internalPointer()
else:
currentTaurusTreeItem = None
if base_previous.isValid():
previousTaurusTreeItem = base_previous.internalPointer()
else:
previousTaurusTreeItem = None
self.currentItemChanged.emit(currentTaurusTreeItem,
previousTaurusTreeItem)
def _updateToolBar(self, current=None, previous=None):
pass
[docs] def selectedItems(self):
"""Returns a list of all selected non-hidden items
:return: (list<TaurusTreeItem>)
"""
view = self.viewWidget()
return [self._mapToSource(index).internalPointer() for index in view.selectedIndexes()]
[docs] def onFilterChanged(self, filter):
if not self.usesProxyQModel():
return
proxy_model = self.getQModel()
if len(filter) > 0 and filter[0] != '^':
filter = '^' + filter
proxy_model.setFilterRegExp(filter)
# proxy_model.setFilterFixedString(filter)
# proxy_model.setFilterWildcard(filter)
# self.update()
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# Perspective handling
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
[docs] def onSwitchPerspective(self, perspective):
old_qmodel = self.getQModel()
self._setPerspective(perspective)
# set the selectables as they where in the previous model
if hasattr(old_qmodel, "selectables"):
self.getQModel().setSelectables(old_qmodel.selectables())
# set the taurus model (if any) to the qmodel
if hasattr(self, 'getModelObj'):
taurusModel = self.getModelObj()
if taurusModel is not None:
self.getQModel().setDataSource(taurusModel)
def _setPerspective(self, perspective):
qmodel_classes = self.KnownPerspectives[perspective]["model"]
# reversed
qmodel_class, qmodel_proxy_classes = qmodel_classes[
-1], qmodel_classes[-2::-1]
qmodel = qmodel_class(parent=self)
qmodel_source = qmodel
if self._proxyModel is None: # applies the chain of proxies
for qmodel_proxy_class in qmodel_proxy_classes:
qproxy = qmodel_proxy_class(self)
qproxy.setSourceModel(qmodel_source)
qmodel_source = qproxy
else:
self._proxyModel.setSourceModel(qmodel_source)
qmodel_source = self._proxyModel
self.setQModel(qmodel_source)
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# QMainWindow overwriting
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
[docs] def addToolBar(self, toolbar):
Qt.QMainWindow.addToolBar(self, toolbar)
self._toolBars.append(toolbar)
[docs] def insertToolBar(self, before, toolbar):
if isinstance(before, Qt.QToolBar):
index = self._toolBars.index(before)
else:
index = before
before = self._toolBars[before]
Qt.QMainWindow.insertToolBar(self, before, toolbar)
self._toolBars.insert(index, toolbar)
[docs]class TaurusBaseModelWidget(TaurusBaseWidget):
"""A class:`taurus.qt.qtgui.base.TaurusBaseWidget` that connects to a
taurus model. It must be used together with
class:`~taurus.qt.qtgui.base.QBaseModelWidget`"""
def __init__(self, designMode=False): # , perspective=None, proxy=None):
name = self.__class__.__name__
self.call__init__(TaurusBaseWidget, name, designMode=designMode)
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# TaurusBaseWidget overwriting
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
[docs] def setModel(self, m):
TaurusBaseWidget.setModel(self, m)
view, modelObj = self.viewWidget(), self.getModelObj()
model = view.model()
if model is None:
return
model.setDataSource(modelObj)
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# QT property definition
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
#: This property holds the unique URI string representing the model name
#: with which this widget will get its data from. The convention used for
#: the string can be found :ref:`here <model-concept>`.
#:
#: **Access functions:**
#:
#: * :meth:`TaurusBaseWidget.getModel`
#: * :meth:`TaurusBaseWidget.setModel`
#: * :meth:`TaurusBaseWidget.resetModel`
#:
#: .. seealso:: :ref:`model-concept`
model = Qt.pyqtProperty("QString", TaurusBaseWidget.getModel, setModel,
TaurusBaseWidget.resetModel)