Making only one column of a QTreeWidgetItem editable
Asked Answered
P

11

32

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.

Pinebrook answered 10/5, 2010 at 10:40 Comment(0)
W
9

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.

Wallin answered 10/5, 2010 at 12:0 Comment(3)
Not necessarily (if I'm not mistaken). See my answer below.Wirework
This is a lot more rework than the NoEditDelegate solution posted below, which I consider best.Debouch
Thanks @David. I also upvoted the NoEditDelegate solution. It seems neater and more complete and I would recommend it for multiple edit triggers.Wirework
W
33

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.

Wirework answered 14/11, 2012 at 7:22 Comment(5)
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.Debouch
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.Wirework
@Wirework 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.Foretooth
@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 for Qt's discussion and example of how the subclass an item delegate.Wirework
@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<QTreeWidgetItem *> 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.Wirework
P
26

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));
Permanganate answered 11/1, 2011 at 11:22 Comment(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).Axenic
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); }}Suspense
W
9

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.

Wallin answered 10/5, 2010 at 12:0 Comment(3)
Not necessarily (if I'm not mistaken). See my answer below.Wirework
This is a lot more rework than the NoEditDelegate solution posted below, which I consider best.Debouch
Thanks @David. I also upvoted the NoEditDelegate solution. It seems neater and more complete and I would recommend it for multiple edit triggers.Wirework
R
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.

Regret answered 1/12, 2010 at 15:17 Comment(0)
I
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()

Ichabod answered 12/7, 2013 at 15:45 Comment(1)
perfect answer fro meDeva
M
4
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);
}
Mercymerdith answered 11/2, 2011 at 1:21 Comment(2)
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.Incertitude
A workable solution, perfect!Swung
O
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.

Oblige answered 10/3, 2018 at 4:27 Comment(0)
S
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);
}
Suspense answered 2/11, 2017 at 11:9 Comment(0)
F
0

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

Facilitation answered 10/5, 2010 at 11:32 Comment(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?Pinebrook
M
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.

Mazurka answered 10/1, 2011 at 23:59 Comment(1)
Now try pressing F2, and you'll be able to edit the supposedly-non-editable columns ;-)Debouch
I
0

For those looking for a PyQt implementation of Olaf Schumann's answer, a similar approach is possible.

First, we sub-class our custom item delegate, returning None (no editor widget):

from qgis.PyQt.QtWidgets import QStyledItemDelegate


class NonEditableDelegate(QStyledItemDelegate):
    def createEditor(self, _parent, _option, _index):
        return None

Then, we apply the custom delegate to the desired column

tree.setItemDelegateForColumn(0, NonEditableDelegate())
Inseminate answered 7/6, 2023 at 7:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.