使QTreeWidgetItem的一列可编辑

31

我有一个包含两列数据的QTreeWidgetItem,有没有办法只让第二列可编辑?当我进行以下操作时:

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

所有列都变成可编辑状态。

11个回答

31

您可以通过以下方法仅使QTreeWidget中的特定列可编辑:

1)将QTreeWidget的editTriggers属性设置为NoEditTriggers

2)在插入项目时,设置QTreeWidgetItem对象的Qt:ItemIsEditable标志

3)将下面的槽函数连接到QTreeWidget对象的“itemDoubleClicked”信号:

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

“isEditable”是您编写的一个函数,对可编辑列返回true,对不可编辑列返回false。


在我的情况下,最佳解决方案。谢谢! - Gustavo Vargas
9
双击并不是唯一开始编辑的方法,还有很多其他的编辑触发器(任意键、编辑键(Windows/Linux上的F2键)、当前项更改等),具体取决于配置(setEditTriggers)。因此,这似乎有些不完整。 - David Faure
我同意,@DavidFaure。我的解决方案易于理解且经过充分测试,但需要一些工作才能实现多个编辑触发器。如果需要多个编辑触发器,我建议使用用户571167的NoEditDelegate解决方案。 - d11
@d11 一旦它变得可编辑,是否可以设置输入策略?(使用 QRegExp 和 QRegExpValidator)例如,只有数字是有效的,否则不接受输入?我无法弄清楚。 - Bear
@bear 在你的情况下,优雅的解决方案是像user571167提供的解决方案一样子类化项目委托,但不是实现NoEditDelegate,而是将在createEditor中创建的编辑器连接到你的QRegExpValidator。请参见此处获取Qt关于如何子类化项目委托的讨论和示例。 - d11
@bear 另一种解决方案是将一个槽连接到 QTreeWidget::itemChanged 并在那里实现您的验证。为了防止意外验证其他项目,您可以在调用我回答中的 editItem 之前将指向正在编辑的项目的指针添加到 QSet<QTreeWidgetItem *> 集合中。在连接到 QTreeWidget::itemChanged 的槽中,检查更改的项目是否在此集合中。如果是,则执行验证并从集合中删除它。如果您需要更完整的示例代码,请提出一个新问题并在此处发布该问题的链接。 - d11

25

最近我遇到同样的问题,并发现了一个适用于所有EditTriggers(而不仅仅是DoubleClicked)且不需要连接双击信号的解决方案。

创建一个代理,为编辑器返回一个空指针:

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

然后将其用作列的自定义委托

ui->parameterView->setItemDelegateForColumn(0, new NoEditDelegate(this));

2
很好的选择。您还可以将委托分配给整个视图,并通过检查 index.column() 是否为阻止列来进行检查。您还可以通过将 index.internalPointer() 重新转换为 QTreeWidgetItem* 来访问 QTreeWidgetItem 本身,以便更多地控制何时阻止编辑,例如仅在该项具有子项时阻止编辑(如我的情况)。 - Phlucious
非常好的答案,稍作微调,您就可以将此委托用于所有操作: 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

9

看起来你需要放弃使用 QTreeWidgetQTreeWidgetItem,并使用 QTreeViewQAbstractItemModel。 "Widget" 类是便利类,是更抽象但更灵活的版本的具体实现。QAbstractItemModel 有一个调用 flags(QModelIndex index) 的方法,在这个方法中,你需要返回适合你列的适当值。


2
不一定(如果我没记错的话)。请看下面我的回答。 - d11
这比下面发布的NoEditDelegate解决方案需要更多的重新工作,我认为这是最好的。 - David Faure
感谢@David。我也点赞了NoEditDelegate的解决方案。它看起来更整洁、更完整,我会推荐它用于多个编辑触发器。 - d11

8

似乎标准的QTreeWidget不允许这样做。我认为有两种方法可以解决这个问题:

  1. 使用一个继承自QAbstractItemModel类并重写flags函数的自定义类来使用QTreeView

  2. 使用一个QStandardItemModel的QTreeView。然后当您添加项目时,只需设置适当的列以允许编辑:

以下是第二个选项的一些代码:

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

我认为第二种方法更简单,但这取决于您对模型的灵活性要求有多高。

6
我发现最简单的方法是使用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);
    }
}
if语句的顶部通过OR添加编辑功能,底部使用AND检查是否存在,然后使用XOR删除它。
这样,当您需要时添加编辑功能,不需要时就可以将其删除。
然后将此函数连接到树部件的itemDoubleClicked()信号,并在isEditable()内编写“编辑还是不编辑”的决策。

对我来说是完美的答案。 - NDestiny

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

QTreeWidget 中:
myQTreeWidget::myQTreeWidget()
{
    EditorDelegate *d = new EditorDelegate(this);
    this->setItemDelegate(d);
}

两件事情:如果列不应该被编辑,从createEditor()返回nullptr,可能作为else的一部分。对于应该被编辑的项目,将项目标志设置为Qt::ItemIsEditable,否则委托将不会被调用。 - CJCombrink
一个可行的解决方案,太棒了! - maxpayne

3
也许有些晚了,但这可能会有所帮助:
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);
    } 
}

这里的0是指您要将其设置为只读列的索引。

flags & (~Qt::ItemIsEditable)

将ItemIsEditable位置设置为0,无论您的项目的先前标志如何。
flags | Qt::ItemIsEditable

设置它为1,无论先前的标志如何。

1
我发现下面的代码完全符合我的需求,并且“有点”可以阻止用户编辑列的某些部分:
基本上,我会检查角色和列。我只允许在第0列中进行编辑。因此,如果用户在任何其他列中进行编辑,则我停止setData编辑,不会进行任何更改。
void treeItemSubclassed::setData(int column, int role, const QVariant &value) {
    if (role == Qt::ItemIsEditable && column != 0){
        return;
    }
    QTreeWidgetItem::setData(column, role, value);
}

0

我对PySide和Python一般都很陌生,但是我通过在QTreeWidget中注册itemClicked回调函数来使其工作。在回调函数内部,检查列并且只有当它是你想允许编辑的列时才调用'editItem'。

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

如果不为列0调用editItem,则该事件基本上被丢弃了。


1
现在尝试按下F2键,你就可以编辑那些原本不可编辑的列了;-) - David Faure

0

根据行和列,设置树状小部件的子项(树的项目)是否可编辑。


1
我该怎么做?QTreeWidgetItem::setFlags不接受列作为参数。如果是在QTreeWidget中进行操作,那么应该使用哪种方法? - Andreas Brinck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接