27

I have a QTreeWidgetItem with two columns of data, is there any way to make only the second column editable? When I do the following:

QTreeWidgetItem* item = new QTreeWidgetItem();
item->setFlags(item->flags() | Qt::ItemIsEditable);

all columns become editable.

Jonathan Mee
  • 35,107
  • 16
  • 95
  • 241
Andreas Brinck
  • 47,252
  • 14
  • 79
  • 112

10 Answers10

29

You can make only certain columns in a QTreeWidget editable using a workaround:

1) Set the editTriggers property of the QTreeWidget to NoEditTriggers

2) On inserting items, set the Qt:ItemIsEditable flag of the QTreeWidgetItem object

3) Connect the following slot to the "itemDoubleClicked" signal of the QTreeWidget object:

void MainWindow::onTreeWidgetItemDoubleClicked(QTreeWidgetItem * item, int column)
{
    if (isEditable(column)) {
        ui.treeWidget->editItem(item, column);
    }
}

where "isEditable" is a function you wrote that returns true for editable columns and false for non-editable columns.

d11
  • 1,108
  • 14
  • 14
  • Best solution in my case. Thanks! – Gustavo Vargas Mar 02 '15 at 01:18
  • 8
    Double-clicking is not the only way to start editing though, there are lots of other edit triggers (any key, the edit key (F2 on Windows/Linux), on current item changed, etc. etc.) depending on the configuration (setEditTriggers). So this seems a bit incomplete. – David Faure Aug 20 '15 at 12:48
  • I agree, @DavidFaure. My solution is simple to understand and well tested, but would require some work to implement for multiple edit triggers. If multiple edit triggers are desired, I recommend the NoEditDelegate solution by user571167. – d11 Dec 01 '15 at 08:53
  • @d11 once it become editable, is it possible to set policy for entering?(Using QRegExp and QRegExpValidator) like only numbers are valid otherwise, don't accept the entry? i can't figure that out. – Bear May 21 '16 at 22:28
  • @bear The elegant solution in your case would be to subclass the item delegate as in the solution by user571167, but instead of implementing a NoEditDelegate, connect the editor you create in `createEditor` to your `QRegExpValidator`. See [here](http://doc.qt.io/qt-5/model-view-programming.html#delegate-classes) for Qt's discussion and example of how the subclass an item delegate. – d11 May 23 '16 at 08:30
  • @bear The alternative solution would be to connect a slot to `QTreeWidget::itemChanged` and to implement your validation there. To prevent accidentally validating other items, you can add a pointer to the item being edited to a `QSet` set just before calling `editItem` in my answer. In the slot connected to `QTreeWidget::itemChanged`, check whether the item that was changed is in this set. If it is, perform the validation and remove it from the set. If you need more complete example code, please ask a new question and post a link to that question here. – d11 May 23 '16 at 08:31
22

I had the same problem recently and discovered a solution which works with all EditTriggers, not only the DoubleClicked one (and the connection to the double clicked signal)

Create a Delegate, that returns a NULL Pointer for the editor:

class NoEditDelegate: public QStyledItemDelegate {
    public:
      NoEditDelegate(QObject* parent=0): QStyledItemDelegate(parent) {}
      virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
        return 0;
      }
    };

And later use it as a custom delegate for your column

ui->parameterView->setItemDelegateForColumn(0, new NoEditDelegate(this));
user571167
  • 221
  • 2
  • 3
  • 2
    Great option. You can also assign the delegate to the entire view and check whether it's a blocked column by checking against `index.column()`. You can also gain access to the QTreeWidgetItem itself by recasting `index.internalPointer()` to a `QTreeWidgetItem*` for even more control over when editing is blocked, such as only blocking editing when the item has children (as in my case). – Phlucious Jun 23 '16 at 23:21
  • great answer, a small fine tune and you can use this delegate for all : virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.column() >0) { return 0; } return QStyledItemDelegate::createEditor(parent, option, index); }} – Dariusz Mar 06 '18 at 12:47
8

Looks like you will have to forgo using QTreeWidget and QTreeWidgetItem and go with QTreeView and QAbstractItemModel. The "Widget" classes are convenience classes that are concrete implementations of the more abstract but more flexible versions. QAbstractItemModel has a call flags(QModelIndex index) where you would return the appropriate value for your column.

Harald Scheirich
  • 9,346
  • 25
  • 51
  • 2
    Not necessarily (if I'm not mistaken). See my answer below. – d11 Nov 14 '12 at 07:25
  • This is a lot more rework than the NoEditDelegate solution posted below, which I consider best. – David Faure Aug 20 '15 at 12:54
  • Thanks @David. I also upvoted the NoEditDelegate solution. It seems neater and more complete and I would recommend it for multiple edit triggers. – d11 Dec 01 '15 at 08:50
8

Seem like the standard QTreeWidget doesn't allow this. I think there are two ways to do this:

  1. Use a QTreeView with your own class derived from QAbstractItemModel and override the flags function

  2. Use a QTreeView with a QStandardItemModel. Then when you add the item just set the appropriate column to allow edits:

Here's some code for the second option:

QString x, y;
QList<QStandardItem*> newIt;
QStandardItem * item = new QStandardItem(x);
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
newIt.append(item);
item = new QStandardItem(y);
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsEditable);
newIt.append(item);
model->appendRow(newIt);

I find the second approach simpler but that depends on how much flexibility you want with your model.

Blaise
  • 175
  • 2
  • 7
6

The simplest way that I found was to use Qt::ItemFlags

void myClass::treeDoubleClickSlot(QTreeWidgetItem *item, int column)
{
    Qt::ItemFlags tmp = item->flags();
    if (isEditable(item, column)) {
        item->setFlags(tmp | Qt::ItemIsEditable);
    } else if (tmp & Qt::ItemIsEditable) {
        item->setFlags(tmp ^ Qt::ItemIsEditable);
    }
}

The top of the if adds the editing functionality through an OR, and the bottom checks if it is there with AND, then removes it with a XOR.

This way the editing functionality is added when you want it, and removed when you don't.

Then connect this function to the tree widget's itemDoubleClicked() signal, and write your 'to edit or not to edit' decision inside of isEditable()

clark
  • 335
  • 4
  • 11
3

Maybe a little late, but may help :

void MyClass::on_treeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column) {
    Qt::ItemFlags flags = item->flags();
    if(column == 0)
    {
        item->setFlags(flags & (~Qt::ItemIsEditable));
    }
    else
    {
        item->setFlags(flags | Qt::ItemIsEditable);
    } 
}

Here 0 is the index of the column you want to make readonly.

flags & (~Qt::ItemIsEditable)

Sets the ItemIsEditable position to 0 regardless the previous flag of your item.

flags | Qt::ItemIsEditable

Sets it to 1 regardless the previous flag.

chraz
  • 124
  • 5
3
class EditorDelegate : public QItemDelegate
{
    Q_OBJECT

public:
    EditorDelegate(QObject *parent):QItemDelegate(parent){};
    QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};

QWidget* EditorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if(index.column() == 1)
    {
        return QItemDelegate::createEditor(parent, option, index);
    }
    return nullptr;
}

In the QTreeWidget:

myQTreeWidget::myQTreeWidget()
{
    EditorDelegate *d = new EditorDelegate(this);
    this->setItemDelegate(d);
}
cbuchart
  • 8,748
  • 6
  • 43
  • 72
  • Two things: return `nullptr` from createEditor() if the colums should not be edited, perhaps as part of the `else`. And set the item flags to `Qt::ItemIsEditable` for the items that should be edited otherwise the delegate will not be called. – CJCombrink May 19 '16 at 12:01
1

I found out that the code below works well for my needs and does "kinda" stop the user from editing certain parts of columns:

I basically check for role and then column. I only allow for editing in column 0. So if user edit it in any other column, then I stop the setData edit and no change is being made.

void treeItemSubclassed::setData(int column, int role, const QVariant &value) {
    if (role == Qt::ItemIsEditable && column != 0){
        return;
    }
    QTreeWidgetItem::setData(column, role, value);
}
Dariusz
  • 583
  • 7
  • 22
0

Set the child of the tree-widget editable or not(itmes of tree), based on the row and column.

Naruto
  • 8,950
  • 32
  • 110
  • 191
  • 1
    How do I do this? `QTreeWidgetItem::setFlags` doesn't take column as an argument. Am I supposed to do this in the `QTreeWidget`, if so with which method? – Andreas Brinck May 10 '10 at 11:46
0

I'm new to PySide and Python in general, but I was able to get this to work by registering with the QTreeWidget for itemClicked callbacks. Within the callback, check the column and only call 'editItem' if it's for a column you want to allow editing.

class Foo(QtGui.QMainWindow):
...
def itemClicked(self, item, column):
   if column > 0:
      self.qtree.editItem(item, column)

By not invoking editItem for column 0, the event is basically discarded.

Kerry
  • 287
  • 1
  • 8