From 1eabbfbfcf9da58975c00dbc190e198c28473921 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 30 Apr 2014 07:32:51 +0200 Subject: [PATCH] Replace QAbstractItemModel by QStandardItemModel. This is probably the most depressing commit ever. For some reason I didn't find this while searching for models I could use and rolled my own one. This uses A LOT less code, and is A LOT faster... --- TODO | 2 - qutebrowser/models/basecompletion.py | 308 ++----------------------- qutebrowser/models/completionfilter.py | 6 +- qutebrowser/widgets/_completion.py | 2 + 4 files changed, 21 insertions(+), 297 deletions(-) diff --git a/TODO b/TODO index 893ba19c8..8f51539c5 100644 --- a/TODO +++ b/TODO @@ -2,13 +2,11 @@ Bugs ==== All kind of FIXMEs -Changing completion models is awfully slow Style ===== Refactor completion widget mess (initializing / changing completions) -we probably could replace CompletionModel with QStandardModel... _foo = QApplication.instance().obj.foo for global singletons? decorators for often-used signals from singletons? - @on_config_changed('colors') diff --git a/qutebrowser/models/basecompletion.py b/qutebrowser/models/basecompletion.py index 6c63fcd81..06090a10e 100644 --- a/qutebrowser/models/basecompletion.py +++ b/qutebrowser/models/basecompletion.py @@ -21,8 +21,8 @@ Module attributes: ROLE_MARKS: The role index used for marks. """ -import logging -from PyQt5.QtCore import Qt, QVariant, QAbstractItemModel, QModelIndex +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QStandardItemModel, QStandardItem ROLE_MARKS = Qt.UserRole @@ -34,38 +34,17 @@ class NoCompletionsError(Exception): pass -class BaseCompletionModel(QAbstractItemModel): +class BaseCompletionModel(QStandardItemModel): - """A simple tree model based on Python OrderdDict containing tuples. + """A simple QStandardItemModel adopted for completions. - Used for showing completions later in the CompletionView. - - Attributes: - _id_map: A mapping from Python object IDs (from id()) to objects, to be - used as internalIndex for the model. - _root: The root item. + Used for showing completions later in the CompletionView. Supports setting + marks and adding new categories/items easily. """ def __init__(self, parent=None): super().__init__(parent) - self._id_map = {} - self._root = CompletionItem([''] * 2) - self._id_map[id(self._root)] = self._root - - def _node(self, index): - """Return the interal data representation for index. - - Args: - index: The QModelIndex to get data for. - - Return: - The CompletionItem for index, or the root CompletionItem if the - index was invalid. - """ - if index.isValid(): - return self._id_map[index.internalId()] - else: - return self._root + self.setColumnCount(2) def _get_marks(self, needle, haystack): """Return the marks for needle in haystack. @@ -99,7 +78,7 @@ class BaseCompletionModel(QAbstractItemModel): cat = self.index(i, 0) for k in range(self.rowCount(cat)): idx = self.index(k, 0, cat) - old = self.data(idx).value() + old = self.data(idx) marks = self._get_marks(needle, old) self.setData(idx, marks, ROLE_MARKS) @@ -110,11 +89,10 @@ class BaseCompletionModel(QAbstractItemModel): name: The name of the category to add. Return: - The created CompletionItem. + The created QStandardItem. """ - cat = CompletionItem([name], self._root) - self._id_map[id(cat)] = cat - self._root.children.append(cat) + cat = QStandardItem(name) + self.appendRow(cat) return cat def new_item(self, cat, name, desc=''): @@ -124,131 +102,12 @@ class BaseCompletionModel(QAbstractItemModel): cat: The parent category. name: The name of the item. desc: The description of the item. - - Return: - The created CompletionItem. """ - item = CompletionItem((name, desc), parent=cat) - self._id_map[id(item)] = item - cat.children.append(item) - return item - - def removeRows(self, position=0, count=1, parent=QModelIndex()): - """Remove rows from the model. - - Override QAbstractItemModel::removeRows. - - Args: - position: The start row to remove. - count: How many rows to remove. - parent: The parent QModelIndex. - """ - node = self._node(parent) - self.beginRemoveRows(parent, position, position + count - 1) - node.children.pop(position) - self.endRemoveRows() - - def columnCount(self, _parent=QModelIndex()): - """Return the column count in the model. - - Override QAbstractItemModel::columnCount. - - Args: - parent: The parent for which to return the column count. Currently - ignored. - - Return: - Column count as an integer. - """ - return self._root.column_count() - - def rowCount(self, parent=QModelIndex()): - """Return the children count of an item. - - Use the root frame if parent is invalid. - Override QAbstractItemModel::rowCount. - - Args: - parent: The parent for which to return the row count. - - Return: - Row count as an integer. - """ - if parent.column() > 0: - return 0 - - if not parent.isValid(): - pitem = self._root - else: - pitem = self._id_map[parent.internalId()] - return len(pitem.children) - - def data(self, index, role=Qt.DisplayRole): - """Return the data for role/index as QVariant. - - Override QAbstractItemModel::data. - - Args: - index: The QModelIndex for which to get data for. - role: The role to use for the data. - - Return: - The data as QVariant or an invalid QVariant on error. - """ - if not index.isValid(): - return QVariant() - try: - item = self._id_map[index.internalId()] - except KeyError: - return QVariant() - try: - return QVariant(item.data(index.column(), role)) - except (IndexError, ValueError): - return QVariant() - - def headerData(self, section, orientation, role=Qt.DisplayRole): - """Return the header data for role/index as QVariant. - - Override QAbstractItemModel::headerData. - - Args: - section: The section (as int) for which to get the header data. - orientation: Qt.Vertical or Qt.Horizontal - - Return: - The data as QVariant or an invalid QVariant on error. - """ - if orientation == Qt.Horizontal and role == Qt.DisplayRole: - return QVariant(self._root.data(section)) - return QVariant() - - def setData(self, index, value, role=Qt.EditRole): - """Set the data for role/index to value. - - Override QAbstractItemModel::setData. - - Args: - index: The QModelIndex where to set the data. - value: The new data value. - role: The role to set the data for. - - Return: - True on success, False on failure. - - Emit: - dataChanged when the data was changed. - """ - if not index.isValid(): - return False - item = self._id_map[index.internalId()] - try: - item.setdata(index.column(), value, role) - except (IndexError, ValueError): - return False - # We explicitely need to select this version, see: - # http://python.6.x6.nabble.com/Bug-Report-pyqt5-discard-silently-signal-with-missing-optional-parameters-dataChanged-roles-for-exam-tt5043737.html - self.dataChanged.emit(index, index, []) - return True + nameitem = QStandardItem(name) + descitem = QStandardItem(desc) + idx = cat.rowCount() + cat.setChild(idx, 0, nameitem) + cat.setChild(idx, 1, descitem) def flags(self, index): """Return the item flags for index. @@ -270,61 +129,6 @@ class BaseCompletionModel(QAbstractItemModel): # category return Qt.NoItemFlags - def index(self, row, column, parent=QModelIndex()): - """Return the QModelIndex for row/column/parent. - - Override QAbstractItemModel::index. - - Args: - row: The row (int) to get an index for. - column: The column (int) to get an index for. - parent: The parent (QModelIndex) to get an index for. - - Return: - A generated QModelIndex or an invalid QModelIndex on failure. - """ - if parent.model() is not None and parent.model() is not self: - logging.warn("index() called with wrong parent! - " - "row {}, column {}, parentmodel {}, self {}".format( - row, column, parent.model(), self)) - return - if (0 <= row < self.rowCount(parent) and - 0 <= column < self.columnCount(parent)): - pass - else: - return QModelIndex() - - if not parent.isValid(): - parent_item = self._root - else: - parent_item = self._id_map[parent.internalId()] - - child_item = parent_item.children[row] - if child_item: - index = self.createIndex(row, column, id(child_item)) - self._id_map.setdefault(index.internalId(), child_item) - return index - else: - return QModelIndex() - - def parent(self, index): - """Return the QModelIndex of the parent of the object behind index. - - Override QAbstractItemModel::parent. - - Args: - index: The QModelIndex to get the parent for. - - Return: - The parent's QModelIndex or an invalid QModelIndex on failure. - """ - if not index.isValid(): - return QModelIndex() - item = self._id_map[index.internalId()].parent - if item == self._root or item is None: - return QModelIndex() - return self.createIndex(item.row(), 0, id(item)) - def sort(self, column, order=Qt.AscendingOrder): """Sort the data in column according to order. @@ -334,83 +138,3 @@ class BaseCompletionModel(QAbstractItemModel): NotImplementedError, should be overwritten in a superclass. """ raise NotImplementedError - - -class CompletionItem(): - - """An item (row) in a CompletionModel. - - Attributes: - parent: The parent of this item. - children: The children of this item. - _data: The data of this item. - _marks: The marks of this item. - """ - - def __init__(self, data, parent=None): - """Constructor for CompletionItem. - - Args: - data: The data for the model, as tuple (columns). - parent: An optional parent item. - """ - self.parent = parent - self.children = [] - self._data = data - self._marks = [] - - def data(self, column, role=Qt.DisplayRole): - """Get the data for role/column. - - Args: - column: The column (int) to get data for. - role: The role to get data for. - - Return: - The requested data. - - Raise: - ValueError if the role is invalid. - """ - if role == Qt.DisplayRole: - return self._data[column] - elif role == ROLE_MARKS: - return self._marks - else: - raise ValueError("Invalid role {}".format(role)) - - def setdata(self, column, value, role=Qt.DisplayRole): - """Set the data for column/role to value. - - Args: - column: The column (int) to set the data for. - value: The value to set the data to. - role: The role to set the data for. - - Raise: - ValueError if the role is invalid. - """ - if role == Qt.DisplayRole: - self._data[column] = value - elif role == ROLE_MARKS: - self._marks = value - else: - raise ValueError("Invalid role {}".format(role)) - - def column_count(self): - """Get the column count in the item. - - Return: - The column count. - """ - return len(self._data) - - def row(self): - """Get the row index (int) of the item. - - Return: - The row index of the item, or 0 if we're a root item. - """ - if self.parent: - return self.parent.children.index(self) - return 0 diff --git a/qutebrowser/models/completionfilter.py b/qutebrowser/models/completionfilter.py index 3119b1404..e8efc2c19 100644 --- a/qutebrowser/models/completionfilter.py +++ b/qutebrowser/models/completionfilter.py @@ -106,7 +106,7 @@ class CompletionFilterModel(QSortFilterProxyModel): if parent == QModelIndex(): return True idx = self.srcmodel.index(row, 0, parent) - data = self.srcmodel.data(idx).value() + data = self.srcmodel.data(idx) # TODO more sophisticated filtering if not self.pattern: return True @@ -125,8 +125,8 @@ class CompletionFilterModel(QSortFilterProxyModel): Return: True if left < right, else False """ - left = self.srcmodel.data(lindex).value() - right = self.srcmodel.data(rindex).value() + left = self.srcmodel.data(lindex) + right = self.srcmodel.data(rindex) leftstart = left.startswith(self.pattern) rightstart = right.startswith(self.pattern) diff --git a/qutebrowser/widgets/_completion.py b/qutebrowser/widgets/_completion.py index d0620f183..940896e4f 100644 --- a/qutebrowser/widgets/_completion.py +++ b/qutebrowser/widgets/_completion.py @@ -448,6 +448,8 @@ class _CompletionItemDelegate(QStyledItemDelegate): if index.column() == 0: marks = index.data(ROLE_MARKS) + if marks is None: + return for mark in marks: cur = QTextCursor(self._doc) cur.setPosition(mark[0])