Source code for mesycontrol.basic_tree_model

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# mesycontrol - Remote control for mesytec devices.
# Copyright (C) 2015-2016 mesytec GmbH & Co. KG <info@mesytec.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

__author__ = 'Florian Lüke'
__email__  = 'f.lueke@mesytec.com'

from qt import QtCore
from qt import Qt
import util

QModelIndex = QtCore.QModelIndex

import weakref

[docs]class BasicTreeModel(QtCore.QAbstractItemModel): def __init__(self, parent=None): super(BasicTreeModel, self).__init__(parent) self.log = util.make_logging_source_adapter(__name__, self) self.clear()
[docs] def index(self, row, col, parent=QModelIndex()): try: root = parent.internalPointer() if parent.isValid() else self.root return self.createIndex(row, col, root.children[row]) except IndexError: return QModelIndex()
[docs] def index_for_node(self, node): if node is None: return QModelIndex() return self.createIndex(node.row, 0, node)
[docs] def index_for_ref(self, ref): return self.index_for_node(self.find_node_by_ref(ref))
[docs] def parent(self, idx): node = idx.internalPointer() if idx.isValid() else None if node is None or node.parent is None: return QModelIndex() return self.createIndex(node.parent.row, 0, node.parent)
[docs] def rowCount(self, parent=QModelIndex()): node = parent.internalPointer() if parent.isValid() else self.root return len(node.children)
[docs] def flags(self, idx): if idx.isValid(): try: return idx.internalPointer().flags(idx.column()) except NotImplementedError: pass return super(BasicTreeModel, self).flags(idx)
[docs] def data(self, idx, role=Qt.DisplayRole): if not idx.isValid(): return None return idx.internalPointer().data(idx.column(), role)
[docs] def setData(self, idx, value, role = Qt.EditRole): if idx.isValid(): try: if idx.internalPointer().set_data(idx.column(), value, role): self.dataChanged.emit( self.index(idx.row(), 0, idx.parent()), self.index(idx.row(), self.columnCount(idx.parent()), idx.parent())) except NotImplementedError: pass return super(BasicTreeModel, self).setData(idx, value, role)
[docs] def add_node(self, node, parent_node, row): self.log.debug("add_node: node=%s, parent=%s, row=%d", node, parent_node, row) parent_idx = self.createIndex(parent_node.row, 0, parent_node) self.beginInsertRows(parent_idx, row, row) parent_node.children.insert(row, node) node.parent = parent_node self.endInsertRows()
[docs] def remove_node(self, node): parent_idx = self.createIndex(node.parent.row, 0, node.parent) self.beginRemoveRows(parent_idx, node.row, node.row) node.parent.children.remove(node) node.parent = None self.endRemoveRows()
[docs] def notify_data_changed(self, node, col1=None, col2=None): if col1 is None: col1 = 0 if col2 is None: col2 = self.columnCount() idx1 = self.createIndex(node.row, col1, node) idx2 = self.createIndex(node.row, col2, node) self.dataChanged.emit(idx1, idx2)
[docs] def find_node_by_ref(self, ref): """Find and return the node pointing to the given ref. If no node can be found None is returned. """ return self.root.find_node_by_ref(ref)
[docs] def clear(self): self.beginResetModel() self.root = BasicTreeNode() self.root.model = self self.endResetModel()
[docs]class BasicTreeNode(object): """Support class for implementing the nodes of a Qt tree model.""" def __init__(self, ref=None, parent=None): super(BasicTreeNode, self).__init__() self._model = None self._parent = None self.ref = ref self.parent = parent self.children = list()
[docs] def get_ref(self): return self._ref() if self.has_ref() else None
[docs] def set_ref(self, ref): self._ref = weakref.ref(ref) if ref is not None else None if self.model is not None: self.model.notify_data_changed(self, 0, self.model.columnCount())
[docs] def has_ref(self): return self._ref is not None
[docs] def get_parent(self): return self._parent() if self._parent is not None else None
[docs] def set_parent(self, parent): self._parent = weakref.ref(parent) if parent is not None else None
[docs] def get_row(self): if self.parent is not None: return self.parent.children.index(self) return 0
[docs] def get_model(self): """ Get this nodes model. If no model is set for this node return the parent nodes model. Return None if no model is set for the node hierarchy. """ if self._model is not None: return self._model() if self.parent is not None: return self.parent.model return None
[docs] def set_model(self, model): self._model = weakref.ref(model) if model is not None else None
[docs] def find_node_by_ref(self, ref): """Find and return the node pointing to the given ref. If no node can be found None is returned. """ if ref is None: return None if self.ref is ref: return self for c in self.children: ret = c.find_node_by_ref(ref) if ret is not None: return ret return None
[docs] def append_child(self, child): self.children.append(child) child.parent = self
[docs] def flags(self, column): raise NotImplementedError()
[docs] def data(self, column, role): raise NotImplementedError()
[docs] def set_data(self, column, value, role): raise NotImplementedError()
[docs] def notify_data_changed(self, col1=0, col2=None): """Calls notify_data_changed on the nodes model if a model is set. col1 and col2 specify the first and last column that changed. If col2 is None it will be set to the models column count. If the node has no model this method does nothing. """ if self.model is not None: if col2 is None: col2 = self.model.columnCount() self.model.notify_data_changed(self, col1, col2)
[docs] def notify_all_columns_changed(self): """No argument variant of notify_data_changed(). Useful to connect to signals whose arguments are to be discarded (the args would take the place of col1 and col2 which is not desired). """ self.notify_data_changed()
ref = property(get_ref, set_ref) parent = property(get_parent, set_parent) row = property(get_row) model = property(get_model, set_model)