带有自定义项的 QTreeView

Posted

技术标签:

【中文标题】带有自定义项的 QTreeView【英文标题】:QTreeView with custom items 【发布时间】:2013-06-21 02:52:16 【问题描述】:

我正在使用 PySide 编写我的第一个 Qt 应用程序,但在创建自定义树视图时遇到了一些麻烦。我想在一列中列出我自己的数据。 每个项目都必须有带有工具提示的文本、不同的文本颜色、不同的背景颜色、带有操作和工具提示的图标。

默认树有效。 我有观点:class TreeView(PySide.QtGui.QTreeView): 和型号:class TreeModel(PySide.QtCore.QAbstractItemModel):

如何为我的项目添加不同的图标?

这是我的例子:

import sys
from PySide import QtGui, QtCore


#-------------------------------------------------------------------------------
# my test data
class MyData():
    def __init__(self, txt, parent=None):
        self.txt = txt
        self.parent = parent
        self.child = []
        self.icon = None
        self.index = None

    #---------------------------------------------------------------------------
    def position(self):
        position = 0
        if self.parent is not None:
            count = 0
            children = self.parent.child
            for child in children:
                if child == self:
                    position = count
                    break
                count += 1
        return position

    #---------------------------------------------------------------------------
    # test initialization
    @staticmethod
    def init():
        root = MyData("root")
        for i in range(0, 2):
            child1 = MyData("child %i" % (i), root)
            root.child.append(child1)
            for x in range(0, 2):
                child2 = MyData("child %i %i" % (i, x), child1)
                child1.child.append(child2)

        return root


#-------------------------------------------------------------------------------
class TreeModel(QtCore.QAbstractItemModel):

    #---------------------------------------------------------------------------
    def __init__(self, tree):
        super(TreeModel, self).__init__()
        self.__tree = tree
        self.__current = tree

    #---------------------------------------------------------------------------
    def flags(self, index):
        flag = QtCore.Qt.ItemIsEnabled
        if index.isValid():
            flag |= QtCore.Qt.ItemIsSelectable \
                 | QtCore.Qt.ItemIsUserCheckable \
                 | QtCore.Qt.ItemIsEditable \
                 | QtCore.Qt.ItemIsDragEnabled \
                 | QtCore.Qt.ItemIsDropEnabled
        return flag

    #---------------------------------------------------------------------------
    def index(self, row, column, parent=QtCore.QModelIndex()):
        node = QtCore.QModelIndex()
        if parent.isValid():
            nodeS = parent.internalPointer()
            nodeX = nodeS.child[row]
            node = self.__createIndex(row, column, nodeX)
        else:
            node = self.__createIndex(row, column, self.__tree)
        return node

    #---------------------------------------------------------------------------
    def parent(self, index):
        node = QtCore.QModelIndex()
        if index.isValid():
            nodeS = index.internalPointer()
            parent = nodeS.parent
            if parent is not None:
                node = self.__createIndex(parent.position(), 0, parent)
        return node

    #---------------------------------------------------------------------------
    def rowCount(self, index=QtCore.QModelIndex()):
        count = 1
        node = index.internalPointer()
        if node is not None:
            count = len(node.child)
        return count

    #---------------------------------------------------------------------------
    def columnCount(self, index=QtCore.QModelIndex()):
        return 1

    #---------------------------------------------------------------------------
    def data(self, index, role=QtCore.Qt.DisplayRole):
        data = None
        if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
            node = index.internalPointer()
            data = node.txt

        if role == QtCore.Qt.ToolTipRole:
            node = index.internalPointer()
            data = "ToolTip " + node.txt

        if role == QtCore.Qt.DecorationRole:
            data = QtGui.QIcon("icon.png")
        return data

    #---------------------------------------------------------------------------
    def setData(self, index, value, role=QtCore.Qt.DisplayRole):
        result = True
        if role == QtCore.Qt.EditRole and value != "":
            node = index.internalPointer()
            node.text = value
            result = True
        return result

    #---------------------------------------------------------------------------
    def __createIndex(self, row, column, node):
        if node.index == None:
            index = self.createIndex(row, column, node)
            node.index = index
            icon = QtGui.QIcon("icon.png")
            b = self.setData(index, icon, QtCore.Qt.DecorationRole)
            b = self.setData(index, "ToolTip "+node.txt, QtCore.Qt.ToolTipRole)
        return node.index



#-------------------------------------------------------------------------------
class TreeView(QtGui.QTreeView):
    #---------------------------------------------------------------------------
    def __init__(self, model, parent=None):
        super(TreeView, self).__init__(parent)
        self.__model = model
        self.setModel(model)


        self.setCurrentIndex(self.__model.index(0, 0))
        return




#-------------------------------------------------------------------------------
class MyTree(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MyTree, self).__init__(parent)

        data = MyData.init()
        treeModel = TreeModel(data)
        treeView = TreeView(treeModel)

        self.setCentralWidget(treeView)


#-------------------------------------------------------------------------------
def main():
    app = QtGui.QApplication(sys.argv)
    form = MyTree()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()

【问题讨论】:

【参考方案1】:

我使用自己的 QWidget

import sys
from PySide import QtGui, QtCore


#-------------------------------------------------------------------------------
# my test data
class Icon():
    def __init__(self, icon, tooltip):
        self.pixmap = QtGui.QPixmap(icon)
        self.tooltip = tooltip

#-------------------------------------------------------------------------------
# my test data
class MyData():
    def __init__(self, txt, parent=None):
        self.txt = txt
        self.tooltip = None
        self.parent = parent
        self.child = []
        self.icon = []
        self.index = None
        self.widget = None

    #---------------------------------------------------------------------------
    def position(self):
        position = 0
        if self.parent is not None:
            count = 0
            children = self.parent.child
            for child in children:
                if child == self:
                    position = count
                    break
                count += 1
        return position

    #---------------------------------------------------------------------------
    # test initialization
    @staticmethod
    def init():
        root = MyData("root")
        root.icon.append(Icon("icon.png", "ToolTip icon.png"))
        root.tooltip = "root tooltip"
        for i in range(0, 2):
            child1 = MyData("child %i" % (i), root)
            child1.icon.append(Icon("icon1.png", "ToolTip icon1.png"))
            child1.tooltip = "child1 tooltip"
            root.child.append(child1)
            for x in range(0, 2):
                child2 = MyData("child %i %i" % (i, x), child1)
                child2.icon.append(Icon("icon1.png", "ToolTip icon1.png"))
                child2.icon.append(Icon("icon2.png", "ToolTip icon2.png"))
                child2.tooltip = "child2 tooltip"
                child1.child.append(child2)

        return root

#-------------------------------------------------------------------------------
class TreeViewModel(QtCore.QAbstractItemModel):
    #---------------------------------------------------------------------------
    def __init__(self, tree):
        super(TreeViewModel, self).__init__()
        self.__tree = tree
        self.__current = tree
        self.__view = None

    #---------------------------------------------------------------------------
    def flags(self, index):
        flag = QtCore.Qt.ItemIsEnabled
        if index.isValid():
            flag |= QtCore.Qt.ItemIsSelectable \
                 | QtCore.Qt.ItemIsUserCheckable \
                 | QtCore.Qt.ItemIsEditable \
                 | QtCore.Qt.ItemIsDragEnabled \
                 | QtCore.Qt.ItemIsDropEnabled
        return flag

    #---------------------------------------------------------------------------
    def index(self, row, column, parent=QtCore.QModelIndex()):
        node = QtCore.QModelIndex()
        if parent.isValid():
            nodeS = parent.internalPointer()
            nodeX = nodeS.child[row]
            node = self.__createIndex(row, column, nodeX)
        else:
            node = self.__createIndex(row, column, self.__tree)
        return node

    #---------------------------------------------------------------------------
    def parent(self, index):
        node = QtCore.QModelIndex()
        if index.isValid():
            nodeS = index.internalPointer()
            parent = nodeS.parent
            if parent is not None:
                node = self.__createIndex(parent.position(), 0, parent)
        return node

    #---------------------------------------------------------------------------
    def rowCount(self, index=QtCore.QModelIndex()):
        count = 1
        node = index.internalPointer()
        if node is not None:
            count = len(node.child)
        return count

    #---------------------------------------------------------------------------
    def columnCount(self, index=QtCore.QModelIndex()):
        return 1

    #---------------------------------------------------------------------------
    def data(self, index, role=QtCore.Qt.DisplayRole):
        data = None
        return data

    #---------------------------------------------------------------------------
    def setView(self, view):
        self.__view = view

    #---------------------------------------------------------------------------
    def __createIndex(self, row, column, node):
        if node.index == None:
            index = self.createIndex(row, column, node)
            node.index = index
        if node.widget is None:
            node.widget = Widget(node)
            self.__view.setIndexWidget(index, node.widget)
        return node.index


#-------------------------------------------------------------------------------
class TreeView(QtGui.QTreeView):
    #---------------------------------------------------------------------------
    def __init__(self, model, parent=None):
        super(TreeView, self).__init__(parent)
        self.setModel(model)
        model.setView(self)
        root = model.index(0, 0)
        self.setCurrentIndex(root)
        self.setHeaderHidden(True)

    #---------------------------------------------------------------------------
    def keyPressEvent(self, event):
        k = event.key()
        if k == QtCore.Qt.Key_F2:
            self.__editMode()

        super(TreeView, self).keyPressEvent(event)

    #---------------------------------------------------------------------------
    def __editMode(self):
        index = self.currentIndex()
        node = index.internalPointer()
        node.widget.editMode(True, True)


#-------------------------------------------------------------------------------
class Label(QtGui.QLabel):
    #---------------------------------------------------------------------------
    def __init__(self, parent, text):
        super(Label, self).__init__(text)
        self.__parent = parent

    #---------------------------------------------------------------------------
    def mouseDoubleClickEvent(self, event):
        #print("mouseDoubleClickEvent")
        if self.__parent is not None:
            self.__parent.editMode(True, True)
        else:
            super(Label, self).mouseDoubleClickEvent(event)


#-------------------------------------------------------------------------------
class LineEdit(QtGui.QLineEdit):
    #---------------------------------------------------------------------------
    def __init__(self, parent, text):
        super(LineEdit, self).__init__(text)
        self.__parent = parent
        self.editingFinished.connect(self.__editingFinished)

    #---------------------------------------------------------------------------
    def keyPressEvent(self, event):
        k = event.key()
        if k == QtCore.Qt.Key_Escape:
            print("ESC 2")
            self.__editingFinished(False)
        super(LineEdit, self).keyPressEvent(event)

    #---------------------------------------------------------------------------
    def __editingFinished(self, bCopy=True):
        print("editingFinished")
        self.__parent.editMode(False, bCopy)

#-------------------------------------------------------------------------------
class Widget(QtGui.QWidget):
    #---------------------------------------------------------------------------
    def __init__(self, node):
        super(Widget, self).__init__()
        self.autoFillBackground()
        self.__node = node
        self.__bEditMode = False
        self.__txt = None
        self.__create(self.__node, self.__bEditMode)

    #---------------------------------------------------------------------------
    def __create(self, node, bEditMode):
        layout = QtGui.QHBoxLayout()
        for icon in node.icon:
            label = Label(None, node.txt)
            label.setPixmap(icon.pixmap)
            label.setToolTip("label tooltip %s %s" % (node.txt, icon.tooltip))
            layout.addWidget(label)

        self.__changeTxt(layout, node, bEditMode, False)
        self.setLayout(layout)

    #---------------------------------------------------------------------------
    def __changeTxt(self, layout, node, bEditMode, bCopy):
        if self.__txt is not None:
            if bCopy:
                node.txt = self.__txt.text()
            if isinstance(self.__txt, LineEdit):
                self.__txt.deselect()
            self.__txt.hide()
            layout.removeWidget(self.__txt)
            self.__txt = None

        if bEditMode:
            self.__txt = LineEdit(self, node.txt)
            self.__txt.setFrame(False)
            self.__txt.selectAll()
            QtCore.QTimer.singleShot(0, self.__txt, QtCore.SLOT('setFocus()'));
        else:
            self.__txt = Label(self, node.txt)
        self.__txt.setToolTip("Text tooltip %s %s" % (node.txt, node.tooltip))
        layout.addWidget(self.__txt, 1)

    #---------------------------------------------------------------------------
    def editMode(self, bEditMode, bCopy):
        if self.__bEditMode != bEditMode:
            self.__bEditMode = bEditMode
            layout = self.layout()
            self.__changeTxt(layout, self.__node, bEditMode, bCopy)

#-------------------------------------------------------------------------------
class MyTree(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MyTree, self).__init__(parent)

        data = MyData.init()
        frame = QtGui.QFrame();
        frame.setLayout( QtGui.QHBoxLayout() );

        treeViewModel = TreeViewModel(data)
        treeView = TreeView(treeViewModel)
        frame.layout().addWidget( treeView );

        self.setCentralWidget(frame)

#-------------------------------------------------------------------------------
def main():
    app = QtGui.QApplication(sys.argv)
    form = MyTree()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()

【讨论】:

感谢您再举一个例子。 我打开了一个与此示例相关的主题。请看***.com/questions/20420012/…

以上是关于带有自定义项的 QTreeView的主要内容,如果未能解决你的问题,请参考以下文章

带有自定义项的 GWT 组合框

带有 ListView 自定义项的 Android 小部件

Listview ArrayAdapter 自定义项 onClickListener 仅第一次工作

如何在不破坏内部绑定的情况下从外部初始化自定义项的属性?

使用 QStyledItemDelegates 作为 QListView 中的自定义项

在超集中构建自定义马条形图并添加自定义项以自定义图表