2014-12-02 102 views
1

問題:setItemDelegateForColunm()crasches應用程序,而不堆棧跟蹤

當設置每行或列中的QtstyledItemDelegate上一個TreeView我的應用程序崩潰而沒有任何進一步的信息。儘管如此,爲整個TreeView工程設置QStyledItemDelegate在我看來似乎很奇怪。

有誰知道這是一個已知的bug,還是我失去了一些東西

我使用PySide 1.1.2使用Qt 4.8(修改版本是不是一個真正的選擇,因爲這將是一筆大開銷因爲它是一個分佈式應用程序也是一個集羣,其中版本更新將包括一堆開銷...)

這裏一個「最小運行例如」

from PySide import QtGui, QtCore 
import sys 
import easymodel 

class MainWin(QtGui.QMainWindow): 
    def __init__(self, model, parent=None): 
     super(MainWin, self).__init__(parent) 

     treeview = QtGui.QTreeView() 
     treeview.setItemDelegateForColumn(0,QtGui.QStyledItemDelegate()) 
     treeview.setModel(model) 
     self.setCentralWidget(treeview) 


if __name__ == "__main__": 
    app = QtGui.QApplication(sys.argv) 
    root = easymodel.TreeItem(easymodel.ListItemData(["Test","Values","Perhabs a button"])) 
    model = easymodel.TreeModel(root)  
    for i in range(1,10): 
     easymodel.TreeItem(easymodel.ListItemData(["Test","Values","Perhabs a button"]), 
          parent = root, 
          index=model.index(root.child_count(),1, 
              parent = root.index()))  


mainWin = MainWin(model) 
mainWin.show() 
sys.exit(app.exec_()) 

爲模型我是上運行使用github上提供的storax的easymodel(Easymodel)以及一些擴展。爲了完整起見,這裏的模型,我使用:

"""This module provides a generic interface for tree models 

It centers around the TreeModel, which is used for all kinds of trees. 
The tree gets customized by the TreeItems. Each TreeItem holds 
a specific ItemData subclass. The ItemData is responsible for 
delivering the data. Make sure that all TreeItems in one hierarchy have the same ItemData subclasses or at least the same column_count. 
If not, make sure the data method can handle columns outside their column count. 
If you want to create a tree, create the needed itemdata classes, 
create a root tree item that is parent for all top-level items. 
The root item does not have to provide data, so the data might be None. 
It is advides to use :class:`jukebox.core.gui.treemodel.ListItemData` because the data in the list 
will be used for the headers. 
Then create the tree items with their appropriate data instances. 
Finally create a tree model instance with the root tree item. 
""" 

import abc 

from PySide import QtCore, QtGui 
from types import NoneType 


class ItemData(object): # pragma: no cover 
    """An abstract class that holds data and is used as an interface for TreeItems 

    When subclassing implement :meth:`ItemData.data` and :meth:`ItemData.column_count`. 
    It is advised to reimplement :meth:`ItemData.internal_data` too. 
    """ 
    __metaclass__ = abc.ABCMeta 

    @abc.abstractmethod 
    def data(self, column, role): 
     """Return the data for the specified column and role 

     The column addresses one attribute of the data. 
     When used in a root item, the data should return the horizontal header 
     data. When returning None, the section Number is used (starting at 1) by the treemodel. 
     So if you want an empty header, return an empty string! 

     :param column: the data column 
     :type column: int 
     :param role: the data role 
     :type role: QtCore.Qt.ItemDataRole 
     :returns: data depending on the role 
     :rtype: 
     :raises: None 
     """ 
     pass 

    @abc.abstractmethod 
    def column_count(self,): 
     """Return the number of columns that can be queried for data 

     :returns: the number of columns 
     :rtype: int 
     :raises: None 
     """ 
     pass 

    def internal_data(self,): 
     """Return the internal data of the ItemData 

     E.g. a ListItemData could return the list it uses, a 
     ProjectItemData could return the Project etc. 

     :returns: the data the itemdata uses as information 
     :rtype: None|arbitrary data 
     :raises: None 
     """ 
     return None 

    def flags(self,): 
     """Return the item flags for the item 

     Default is QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable 

     :returns: the item flags 
     :rtype: QtCore.Qt.ItemFlags 
     :raises: None 
     """ 
     return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable 


class ListItemData(ItemData): 
    """Item data for generic lists 

    Initialize it with a list of objects. Each element corresponds to a column. 
    For DisplayRole the objects are converted to strings with ``str()``. 
    """ 

    def __init__(self, liste,seperateListitemsInView=True): 
     """ Constructs a new StringItemData with the given list 

     :param list: a list of objects, one for each column 
     :type list: list of objects 
     :raises: None 
     """ 
     super(ListItemData, self).__init__() 
     self._list = liste 
     self._seperateListitemsInView = seperateListitemsInView 
     self._flags = super(ListItemData, self).flags() 

    def data(self, column, role): 
     """Return the data for the specified column and role 

     For DisplayRole the element in the list will be converted to a sting and returned. 

     :param column: the data column 
     :type column: int 
     :param role: the data role 
     :type role: QtCore.Qt.ItemDataRole 
     :returns: data depending on the role, or None if the column is out of range 
     :rtype: depending on the role or None 
     :raises: None 
     """ 
     displayString = "" 
     if role == QtCore.Qt.DisplayRole: 

      if column == 0 or not self._seperateListitemsInView: 
       return str(self._list[column]) 
      if column > 0 and column < len(self._list): 
       try: 
        for l in self._list[column]: 
         displayString += " %s"%l 
        return displayString 
       except: 
        return str(self._list[column]) 

     if role == QtCore.Qt.BackgroundRole: 
      if type(self._list[-1]) is QtGui.QColor: 
       return self._list[-1] 
      else: 
       return QtGui.QColor().fromRgbF(1.,1.,1.)   

    def column_count(self,): 
     """Return the number of columns that can be queried for data 

     :returns: the number of columns 
     :rtype: int 
     :raises: None 
     """ 
     return len(self._list) 

    def internal_data(self,): 
     """Return the list 

     :returns: the internal list 
     :rtype: :class:`list` 
     :raises: None 
     """ 
     return self._list 

    def addFlag(self,flag): 
     self._flags = self._flags | flag 

    def flags(self,): 
     return self._flags 



class TreeItem(object): 
    """General TreeItem 

    You can represent a tree structure with these tree items. Each item 
    should contain some data that it can give to the model. 
    Note that each tree always has one root item. 
    Even if you have multiple top level items, they are all grouped under one 
    root. The data for the root item can be None but it is advised to use 
    a ListItemData so you can provide horizontal headers. 
    """ 

    def __init__(self, data, parent=None,index = QtCore.QModelIndex()): 
     """Constructs a new TreeItem that holds some data and might be parented under parent 

     :param data: the data item. if the tree item is the root, the data will be used for horizontal headers! 
        It is recommended to use :class:`jukebox.core.gui.treeitem.ListItemData` in that case. 
     :type data: :class:`jukebox.core.gui.treemodel.ItemData` 
     :param parent: the parent treeitem 
     :type parent: :class:`jukebox.core.gui.treemodel.TreeItem` 
     :raises: None 
     """ 
     self._data = data 
     self._parent = parent 
     self._index = index 
     if self._parent is not None: 
      self._parent.childItems.append(self) 
     self.childItems = [] 


    def child(self, row): 
     """Return the child at the specified row 

     :param row: the row number 
     :type row: int 
     :returns: the child 
     :rtype: :class:`jukebox.core.gui.treemodel.TreeItem` 
     :raises: IndexError 
     """ 
     return self.childItems[row] 

    def child_count(self,): 
     """Return the number of children 

     :returns: child coun 
     :rtype: int 
     :raises: None 
     """ 
     return len(self.childItems) 

    def row(self,): 
     """Return the index of this tree item in the parent rows 

     :returns: the row of this TreeItem in the parent 
     :rtype: int 
     :raises: None 
     """ 
     if self._parent is None: 
      return 0 
     return self._parent.childItems.index(self) 

    def column_count(self,): 
     """Return the number of columns that the children have 

     :returns: the column count of the children data 
     :rtype: int 
     :raises: None 
     """ 
     if self.child_count(): 
      return self.childItems[0]._data.column_count() 
     else: 
      return 0 

    def data(self, column, role): 
     """Return the data for the column and role 

     :param column: the data column 
     :type column: int 
     :param role: the data role 
     :type role: QtCore.Qt.ItemDataRole 
     :returns: data depending on the role 
     :rtype: 
     :raises: None 
     """ 
     if self._data is not None and (column >= 0 or column < self._data.column_count()): 
      return self._data.data(column, role) 

    def parent(self,): 
     """Return the parent tree item 

     :returns: the parent or None if there is no parent 
     :rtype: :class:`jukebox.core.gui.treemodel.TreeItem` 
     :raises: None 
     """ 
     return self._parent 

    def itemdata(self,): 
     """Return the internal :class:`ItemData` 

     :returns: the internal ItemData 
     :rtype: :class:`ItemData` 
     :raises: None 
     """ 
     return self._data 

    def internal_data(self,): 
     """Return the internal data of the item data 

     E.g. a ListItemData could return the list it uses, a 
     ProjectItemData could return the Project etc. 

     :returns: the data the itemdata uses as information 
     :rtype: None|arbitrary data 
     :raises: None 
     """ 
     return self._data.internal_data() 

    def flags(self,): 
     """Return the flags for the item 

     :returns: the flags 
     :rtype: QtCore.Qt.ItemFlags 
     :raises: None 
     """ 
     return self._data.flags() 

    def index(self): 
     return self._index 

class TreeModel(QtCore.QAbstractItemModel): 
    """A tree model that uses the :class:`jukebox.core.gui.treemodel.TreeItem` to represent a general tree. 

    """ 

    def __init__(self, root, parent=None): 
     """Constructs a new tree model with the given root treeitem 

     :param root: 
     :type root: 
     :param parent: 
     :type parent: 
     :raises: None 
     """ 
     super(TreeModel, self).__init__(parent) 
     self._root = root 
     # Extension for Temanejo: A list of the propertyRootItems which are sorted in incoming order 
     self.propertyRootItems = [] 

    def index(self, row, column, parent=None): 
     """Returns the index of the item in the model specified by the given row, column and parent index. 

     :param row: the row of the item 
     :type row: int 
     :param column: the column for the item 
     :type column: int 
     :param parent: the parent index 
     :type parent: :class:`QtCore.QModelIndex`: 
     :returns: the index of the item 
     :rtype: :class:`QtCore.QModelIndex` 
     :raises: None 
     """ 
     if parent is None: 
      parent = QtCore.QModelIndex() 
     if not self.hasIndex(row, column, parent): 
      return QtCore.QModelIndex() 

     if parent.isValid(): 
      parentItem = parent.internalPointer() 
     else: 
      parentItem = self._root 

     try: 
      childItem = parentItem.child(row) 
      return self.createIndex(row, column, childItem) 
     except IndexError: 
      return QtCore.QModelIndex() 

    def parent(self, index): 
     """Returns the parent of the model item with the given index. If the item has no parent, an invalid QModelIndex is returned. 

     :param index: the index that you want to know the parent of 
     :type index: :class:`QtCore.QModelIndex` 
     :returns: parent index 
     :rtype: :class:`QtCore.QModelIndex` 
     :raises: None 
     """ 
     if not index.isValid(): 
      return QtCore.QModelIndex() 
     childItem = index.internalPointer() 
     parentItem = childItem.parent() 
     if parentItem is self._root: 
      return QtCore.QModelIndex() 
     return self.createIndex(parentItem.row(), 0, parentItem) 

    def rowCount(self, parent): 
     """Returns the number of rows under the given parent. 
     When the parent is valid it means that rowCount is returning the number 
     of children of parent. 

     :param parent: the parent index 
     :type parent: :class:`QtCore.QModelIndex`: 
     :returns: the row count 
     :rtype: int 
     :raises: None 
     """ 
     if parent.column() > 0: 
      return 0 
     if not parent.isValid(): 
      parentItem = self._root 
     else: 
      parentItem = parent.internalPointer() 
     return parentItem.child_count() 

    def columnCount(self, parent): 
     """Returns the number of columns for the children of the given parent. 

     :param parent: the parent index 
     :type parent: :class:`QtCore.QModelIndex`: 
     :returns: the column count 
     :rtype: int 
     :raises: None 
     """ 
     if parent.isValid(): 
      return parent.internalPointer().column_count() 
     else: 
      return self._root.column_count() 

    def data(self, index, role=QtCore.Qt.DisplayRole): 
     """Returns the data stored under the given role for the item referred to by the index. 

     :param index: the index 
     :type index: QModelIndex 
     :param role: the data role 
     :type role: QtCore.Qt.ItemDataRole 
     :returns: some data depending on the role 
     :raises: None 
     """ 
     if not index.isValid(): 
      return 
     item = index.internalPointer() 
     return item.data(index.column(), role) 

    def headerData(self, section, orientation, role): 
     """ 

     :param section: 
     :type section: 
     :param orientation: 
     :type orientation: 
     :param role: 
     :type role: 
     :returns: None 
     :rtype: None 
     :raises: None 
     """ 
     if orientation == QtCore.Qt.Horizontal: 
      d = self._root.data(section, role) 
      if d is None and role == QtCore.Qt.DisplayRole: 
       return str(section+1) 
      return d 
     if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole: 
      return str(section+1) 

    def insertRow(self, row, item, parent): 
     """Inserts a single item before the given row in the child items of the parent specified. 

     :param row: the index where the rows get inserted 
     :type row: int 
     :param item: the item to insert. When creating the item, make sure it's parent is None. 
        If not it will defeat the purpose of this function. 
     :type item: :class:`TreeItem` 
     :param parent: the parent 
     :type parent: class:`QtCore.QModelIndex` 
     :returns: Returns true if the row is inserted; otherwise returns false. 
     :rtype: bool 
     :raises: None 
     """ 
     parentitem = parent.internalPointer() 
     self.beginInsertRows(parent, row, row) 
     item._parent = parentitem 
     parentitem.childItems.insert(row, item) 
     self.endInsertRows() 
     return True 

    @property 
    def root(self,): 
     """Return the root tree item 

     :returns: the root item 
     :rtype: :class:`TreeItem` 
     :raises: None 
     """ 
     return self._root 

    def flags(self, index): 
     """ 

     :param index: 
     :type index: 
     :returns: None 
     :rtype: None 
     :raises: None 
     """ 
     if index.isValid(): 
      item = index.internalPointer() 
      return item.flags() 
     else: 
      super(TreeModel, self).flags(index) 

在此先感謝

回答

2

你需要保持對委託的引用:

self.delegate = QtGui.QStyledItemDelegate() 
    treeview.setItemDelegateForColumn(0, self.delegate) 

或者給它父:

treeview.setItemDelegateForColumn(0, QtGui.QStyledItemDelegate(treeview)) 
+0

好吧,那很簡單,謝謝! 我沒有得到的是爲什麼我必須這樣做,如果我稱它爲每列或每行,而不是如果我稱它爲整棵樹。 (儘管如此,引用它並不壞...)然後它是否實現了保存? – 2014-12-03 08:26:49

+0

@StefanReinhardt。 Qt並不擁有委託的所有權,所以如果Python垃圾收集器刪除它,Qt可能會嘗試引用不再存在的東西,從而導致崩潰。 Python沒有給出關於什麼時候刪除對象或者以何種順序的任何保證,因此通常很好的做法是明確地保持對對象的引用和/或確保它們具有父對象。 – ekhumoro 2014-12-03 17:38:18