Making only one column of a QTreeWidgetItem editable

自古美人都是妖i 提交于 2019-11-27 22:44:32

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.

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.

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));

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.

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()

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);
}

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.

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.

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);
}

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!