3

So I've been trying to implement a proper TreeView that displays directories and files depending on user input - I am allowing the user to add directories and files to his "project" either recursively or otherwise, after which I create my own tree view of the contents of said project.

Now, my problem is that even though most of the documentation and other questions I've found on this subject seem to want to disable the editability of treeview items, I am trying ( and failing ) to find a way to enable it. What I would like is for the user to be able to double click on any cell in any column of my treeview and then edit its contents. Does anyone know how to do this?

Below is the code I am using to generate a tab in a tabView Widget, after which i then add the TreeView. The items of the TreeView are later added through the AddParent and AddChild methods.

class treeTab(QtWidgets.QWidget):
def __init__(self,core,main,label):
    super (treeTab,self).__init__()
    self.label = label
    self.core = core

    self.sizes = core.UISizes

    self.tab_sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,QtWidgets.QSizePolicy.Expanding)

    self.tree = QtWidgets.QTreeWidget(self)
    self.tree.setColumnCount(len(self.sizes.projectTreeColumnLabels))
    self.tree.setHeaderLabels(self.sizes.projectTreeColumnLabels)
    self.tree.setSizePolicy(self.tab_sizePolicy)
    self.tree_layout = QtWidgets.QGridLayout()
    self.tree_layout.objectName = self.label + "TreeGridLayout"
    self.tree.setLayout(self.tree_layout)
    self.treeroot = self.tree.invisibleRootItem()
    self.tree.setSelectionMode(Qt.QAbstractItemView.ContiguousSelection)

def addParent(self, parent, column, title, data):
    item = QtWidgets.QTreeWidgetItem(parent, [title])
    item.setData(column, QtCore.Qt.UserRole, data)
    item.setChildIndicatorPolicy(QtWidgets.QTreeWidgetItem.ShowIndicator)
    item.setExpanded (True)
    return item

def addChild(self, parent, column, title, data):
    item = QtWidgets.QTreeWidgetItem(parent, [title])
    item.setData(column, QtCore.Qt.UserRole, data)
    item.setText(1,data.print_tags())
    item.setText(2,data.category.name)
    item.setText(3,data.format)
    item.setCheckState (column, QtCore.Qt.Unchecked)
    item.setFlags(item.flags() or QtCore.Qt.ItemIsEditable)
    return item
MaVCArt
  • 745
  • 8
  • 21

2 Answers2

4

You have confused binary operators and Boolean operators. Boolean operators (such as and and or) are used with Boolean values (such as True and False) to produce a single True or False once the expression is evaluated.

However, the flags and not Boolean values. They are integers which are powers of 2 (or binary numbers with only one bit set), such that they can be combined into a single integer that represents whether each flag is enabled or disabled. For instance, 2 is represented in binary as 0b010. 4 is represented as 0b100. If you bitwise or these together, you get 0b110, indicating that both the flag equal to 2 and the flag equal to 4 is set. However the flag equal to 1 is not set (the 0 in the 0b110).

In short, you should set the flags using the bitwise or operator (|):

item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
Community
  • 1
  • 1
three_pineapples
  • 11,579
  • 5
  • 38
  • 75
1

So I managed to figure this one out - it turned out to be quite simple in the end :)

In order to create an 'editable' treeView item where you can double click on the text of a particular item to edit it, you simply need to change the widget contained in that particular column of the item to a QLineEdit Widget that deletes itself upon pressing enter. The code for this looks as follows:

This is the code to connect the double click event to a method that gets the currently selected item and replaces it with a QLineEdit Widget:

self.tree.itemDoubleClicked.connect(self.editItem)

def editItem(self,*args):
    itm = self.tree.itemFromIndex(self.tree.selectedIndexes()[0])
    column = self.tree.currentColumn()
    edit = QtWidgets.QLineEdit()
    edit.returnPressed.connect(lambda*_:self.project.setData(column,edit.text(),itm,column,self.tree))
    edit.returnPressed.connect(lambda*_:self.update())
    self.tree.setItemWidget(itm,column,edit)

Note especially the combination of the following code:

    itm = self.tree.itemFromIndex(self.tree.selectedIndexes()[0])
    column = self.tree.currentColumn()

This code in effect gives you both the row and the column of the currently selected item, which is useful if you want to edit column items separately.

Now You'll be asking yourself why I'm passing so many parameters to the 'setData' method: this is purely for the purposes of my particular project, so don't worry about it. the 'returnPressed' event simply needs to connect to the proper method to handle whatever data it contains, and then delete itself. in my code, this looks like this:

def setData(self,dataTypeIndex,data,item,column,tree):
    if dataTypeIndex == 0:
        # filename
        self.name = data
    elif dataTypeIndex == 1:
        # tags
        data = data.split(",")
        self.tags = []
        for tag in data:
            self.tags.append(Tag(tag))
    elif dataTypeIndex == 2:
        # category
        self.category.name = data

    tree.setItemWidget(item,column,None)

This last line of code ( tree.setItemWidget(item,column,None) ) is where the QlineEdit is unparented and therefore effectively removed.

MaVCArt
  • 745
  • 8
  • 21
  • Quite simple? All you really needed to do was replace `or` with `|`, as per the answer by three_pineapples... – ekhumoro Oct 18 '15 at 02:19
  • Right well in either case my way may be longer but it works as well – MaVCArt Oct 18 '15 at 14:25
  • In fact, using my method you can actually catch the double click event and prevent certain columns from being editable, or implement different methods of editing a certain column's value - say, pop up a new window when double clicking rather than editing whatever value it contains directly. This opens a couple options that simple text editing prevents :) – MaVCArt Oct 19 '15 at 09:35