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)
"""


__docformat__ = "restructuredtext"

from taurus.external.qt import Qt, compat

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] def viewWidget(self): return self._viewWidget
[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 getFilterLineEdit(self): return self._filterLineEdit
[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] def setFilterText(self, text): self.getFilterLineEdit().setText(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] def onAdd(self): self.addTriggered.emit()
[docs] def onRemove(self): self.removeTriggered.emit()
[docs] def onMoveTop(self): self.moveTopTriggered.emit()
[docs] def onMoveUp(self): self.moveUpTriggered.emit()
[docs] def onMoveDown(self): self.moveDownTriggered.emit()
[docs] def onMoveBottom(self): self.moveBottomTriggered.emit()
[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] def onSelectAll(self): self.selectAllTriggered.emit()
[docs] def onclearSelection(self): self.clearSelectionTriggered.emit()
[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] def onRefresh(self): self.refreshTriggered.emit()
[docs] class PerspectiveToolBar(BaseToolBar): perspectiveChanged = Qt.pyqtSignal(compat.PY_OBJECT) 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: the switch perspective tool button :rtype: PyQt5.Qt.QToolButton """ 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] def perspective(self): return self._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(compat.PY_OBJECT, int) itemDoubleClicked = Qt.pyqtSignal(compat.PY_OBJECT, int) itemSelectionChanged = Qt.pyqtSignal() currentItemChanged = Qt.pyqtSignal(compat.PY_OBJECT, compat.PY_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 createViewWidget(self, klass=None): raise NotImplementedError
[docs] def createStatusBar(self): sb = Qt.QStatusBar() sb.setSizeGripEnabled(False) return sb
[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
[docs] def getPerspectiveBar(self): return self._perspectiveBar
[docs] def getFilterBar(self): return self._filterBar
[docs] def getSelectionBar(self): return self._selectionBar
[docs] def getRefreshBar(self): return self._refreshBar
[docs] def onRefreshModel(self): self.getQModel().refresh(True)
[docs] def onSelectAll(self): view = self.viewWidget() view.selectAll()
[docs] def onClearSelection(self): view = self.viewWidget() view.clearSelection()
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())
[docs] def viewWidget(self): return self._viewWidget
[docs] def getQModel(self): return self.viewWidget().model()
[docs] def getBaseQModel(self): return self._baseQModel
[docs] def usesProxyQModel(self): return isinstance(self.getQModel(), Qt.QAbstractProxyModel)
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() 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 viewSelectionChanged(self, selected, deselected): self.itemSelectionChanged.emit()
[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: :rtype: 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()
[docs] def refresh(self): self.getQModel().refresh()
# -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ # Perspective handling # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
[docs] def perspective(self): return self._perspectiveBar.perspective()
[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, **kwargs): TaurusBaseWidget.setModel(self, m, **kwargs) view, modelObj = self.viewWidget(), self.getModelObj(**kwargs) 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, )