QTreeWidget removeItemWidget 后需要删除widget 吗?
Do you need to delete widget after removeItemWidget from QTreeWidget?
我有一个包含两列的 QTreeWidget
:一列用于 属性 名称,一列用于 属性 值。该值可以通过小部件进行编辑。例如一个 属性 是动物。当您双击 属性 值列时,我通过以下代码制作了一个包含不同动物类型的(自定义)组合框:
QTreeWidgetItemComboBox* comboBox = new QTreeWidgetItemComboBox(treeItem, 1);
// treeitem is a pointer to the row that is double clicked
comboBox->addItems(QStringList() << "Bird" << "Fish" << "Ape");
ui.treeWidget->setItemWidget(treeItem, 1, comboBox);
当该行失去焦点时,我再次删除小部件(并且该值作为 QTreeWidgetItem
的文本)。为了删除我使用
ui.treeWidget->removeItemWidget(treeItem, 1);
现在我想知道,既然我已经使用了 new
,我是否还需要 delete
这个小部件。我知道如果您使用 takeChild(i)
就是这种情况。但是我没有看到 itemWidget 的类似内容。
我需要删除它吗?正确的顺序是什么?
QTreeWidgetItemComboBox* comboBox = ui.treeWidget->itemWidget(treeItem,1);
// Do I need a cast here since the return type is QWidget*
ui.treeWidget->removeItemWidget(treeItem, 1);
delete comboBox;
或
QTreeWidgetItemComboBox* comboBox = ui.treeWidget->itemWidget(treeItem,1);
// Do I need a cast here since the return type is QWidget*
delete comboBox;
ui.treeWidget->removeItemWidget(treeItem, 1);
您不能删除项目小部件,因为一旦使用 setItemWidget()
传递给树,树就是小部件的所有者。
来自 setItemWidget()
的文档:
Note: The tree takes ownership of the widget.
编辑:如果您想要一个新的小部件,只需再次调用 setItemWidget()
或调用 removeItemWidget()
以防您不再需要该小部件.这棵树将确保不会丢失任何记忆。
当小部件添加到 QTreeWidget
时,它确实取得了小部件的所有权。但是它只是意味着当父级被销毁时widget将被删除
因此,如果您只是想在保持父 QTreeWidget
活动的同时删除小部件,您确实必须手动删除它。
正确的解决方法是第一种,先从QTreeWidget
中删除widget,然后通过以下方式之一删除它:
delete comboBox;
comboBox = nullptr;
或:
comboBox.deleteLater();
第二个优先
编辑:
我不会更改答案,因为更改已经接受的内容可能是不诚实的,...
但是正如@Scopchanov提到的,通过阅读源代码,QTreeWidget::removeItemWidget()
已经调用了deleteLater()
旧小部件上的方法。我们不必手动完成。
无论如何,the documentation 表示多次调用 deleteLater()
是安全的:
Note: It is safe to call this function more than once; when the first deferred deletion event is delivered, any pending events for the object are removed from the event queue.
因此,调用QTreeWidget::removeItemWidget()
后手动删除widget就没有用了。
说明
您 不应该 手动删除添加到 QTreeWidget 的小部件,因为它 自动删除
- 销毁其父树小部件
这是 Qt 父子机制的直接结果。
- 在树小部件仍然存在的任何时候调用
QTreeWidget::removeItemWidget
。
这个不是很明显,因为 documentation 只是说:
Removes the widget set in the given item in the given column.
但是,查看 source code 就会很清楚确实发生了什么,即
QTreeWidget::removeItemWidget
使用 null
指针调用 QTreeWidget::setItemWidget
(无小部件)
inline void QTreeWidget::removeItemWidget(QTreeWidgetItem *item, int column)
{ setItemWidget(item, column, nullptr); }
QTreeWidget::setItemWidget
依次调用 QAbstractItemView::setIndexWidget
void QTreeWidget::setItemWidget(QTreeWidgetItem *item, int column, QWidget *widget)
{
Q_D(QTreeWidget);
QAbstractItemView::setIndexWidget(d->index(item, column), widget);
}
最后QAbstractItemView::setIndexWidget
检查这个索引处是否已经有一个widget,如果有就调用它的deleteLater
方法
if (QWidget *oldWidget = indexWidget(index)) {
d->persistent.remove(oldWidget);
d->removeEditor(oldWidget);
oldWidget->removeEventFilter(this);
oldWidget->deleteLater();
}
简单地说(这应该在 QTreeWidget
的两种方法的文档中明确说明),任何对 QTreeWidget::setItemWidget
或 QTreeWidget::removeItemWidget
的调用都会删除小部件(如果有的话)已经为项目设置.
例子
这是我为您准备的一个简单示例,用于演示所描述的行为:
#include <QApplication>
#include <QBoxLayout>
#include <QTreeWidget>
#include <QComboBox>
#include <QPushButton>
struct MainWindow : public QWidget
{
MainWindow(QWidget *parent = nullptr) : QWidget(parent) {
auto *l = new QVBoxLayout(this);
auto *treeWidget = new QTreeWidget(this);
auto *item = new QTreeWidgetItem(treeWidget);
auto *button = new QPushButton(tr("Remove combo box"), this);
auto *comboBox = new QComboBox();
comboBox->addItems(QStringList() << "Bird" << "Fish" << "Ape");
treeWidget->setItemWidget(item, 0, comboBox);
l->addWidget(button);
l->addWidget(treeWidget);
connect(comboBox, &QComboBox::destroyed, [](){
qDebug("The combo box is gone.");
});
connect(button, &QPushButton::clicked, [treeWidget, item](){
treeWidget->removeItemWidget(item, 0);
});
resize(400, 300);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
结果
可以使用应用程序测试所描述的销毁小部件的方法
- 简单地关闭 window 会破坏树部件及其子组合框,因此会发出组合框的销毁信号并且 lambda 打印
The combo box is gone.
- 按下按钮后,调用连接到其
clicked
信号的 lambda 函数,从树形小部件中删除组合框。因为组合框也被(自动)删除,所以调用了第二个 connect
语句的 lambda,它也打印了
The combo box is gone.
我有一个包含两列的 QTreeWidget
:一列用于 属性 名称,一列用于 属性 值。该值可以通过小部件进行编辑。例如一个 属性 是动物。当您双击 属性 值列时,我通过以下代码制作了一个包含不同动物类型的(自定义)组合框:
QTreeWidgetItemComboBox* comboBox = new QTreeWidgetItemComboBox(treeItem, 1);
// treeitem is a pointer to the row that is double clicked
comboBox->addItems(QStringList() << "Bird" << "Fish" << "Ape");
ui.treeWidget->setItemWidget(treeItem, 1, comboBox);
当该行失去焦点时,我再次删除小部件(并且该值作为 QTreeWidgetItem
的文本)。为了删除我使用
ui.treeWidget->removeItemWidget(treeItem, 1);
现在我想知道,既然我已经使用了 new
,我是否还需要 delete
这个小部件。我知道如果您使用 takeChild(i)
就是这种情况。但是我没有看到 itemWidget 的类似内容。
我需要删除它吗?正确的顺序是什么?
QTreeWidgetItemComboBox* comboBox = ui.treeWidget->itemWidget(treeItem,1);
// Do I need a cast here since the return type is QWidget*
ui.treeWidget->removeItemWidget(treeItem, 1);
delete comboBox;
或
QTreeWidgetItemComboBox* comboBox = ui.treeWidget->itemWidget(treeItem,1);
// Do I need a cast here since the return type is QWidget*
delete comboBox;
ui.treeWidget->removeItemWidget(treeItem, 1);
您不能删除项目小部件,因为一旦使用 setItemWidget()
传递给树,树就是小部件的所有者。
来自 setItemWidget()
的文档:
Note: The tree takes ownership of the widget.
编辑:如果您想要一个新的小部件,只需再次调用 setItemWidget()
或调用 removeItemWidget()
以防您不再需要该小部件.这棵树将确保不会丢失任何记忆。
当小部件添加到 QTreeWidget
时,它确实取得了小部件的所有权。但是它只是意味着当父级被销毁时widget将被删除
因此,如果您只是想在保持父 QTreeWidget
活动的同时删除小部件,您确实必须手动删除它。
正确的解决方法是第一种,先从QTreeWidget
中删除widget,然后通过以下方式之一删除它:
delete comboBox;
comboBox = nullptr;
或:
comboBox.deleteLater();
第二个优先
编辑:
我不会更改答案,因为更改已经接受的内容可能是不诚实的,...
但是正如@Scopchanov提到的,通过阅读源代码,QTreeWidget::removeItemWidget()
已经调用了deleteLater()
旧小部件上的方法。我们不必手动完成。
无论如何,the documentation 表示多次调用 deleteLater()
是安全的:
Note: It is safe to call this function more than once; when the first deferred deletion event is delivered, any pending events for the object are removed from the event queue.
因此,调用QTreeWidget::removeItemWidget()
后手动删除widget就没有用了。
说明
您 不应该 手动删除添加到 QTreeWidget 的小部件,因为它 自动删除
- 销毁其父树小部件
这是 Qt 父子机制的直接结果。
- 在树小部件仍然存在的任何时候调用
QTreeWidget::removeItemWidget
。
这个不是很明显,因为 documentation 只是说:
Removes the widget set in the given item in the given column.
但是,查看 source code 就会很清楚确实发生了什么,即
QTreeWidget::removeItemWidget
使用null
指针调用QTreeWidget::setItemWidget
(无小部件)inline void QTreeWidget::removeItemWidget(QTreeWidgetItem *item, int column) { setItemWidget(item, column, nullptr); }
QTreeWidget::setItemWidget
依次调用QAbstractItemView::setIndexWidget
void QTreeWidget::setItemWidget(QTreeWidgetItem *item, int column, QWidget *widget) { Q_D(QTreeWidget); QAbstractItemView::setIndexWidget(d->index(item, column), widget); }
最后
QAbstractItemView::setIndexWidget
检查这个索引处是否已经有一个widget,如果有就调用它的deleteLater
方法if (QWidget *oldWidget = indexWidget(index)) { d->persistent.remove(oldWidget); d->removeEditor(oldWidget); oldWidget->removeEventFilter(this); oldWidget->deleteLater(); }
简单地说(这应该在 QTreeWidget
的两种方法的文档中明确说明),任何对 QTreeWidget::setItemWidget
或 QTreeWidget::removeItemWidget
的调用都会删除小部件(如果有的话)已经为项目设置.
例子
这是我为您准备的一个简单示例,用于演示所描述的行为:
#include <QApplication>
#include <QBoxLayout>
#include <QTreeWidget>
#include <QComboBox>
#include <QPushButton>
struct MainWindow : public QWidget
{
MainWindow(QWidget *parent = nullptr) : QWidget(parent) {
auto *l = new QVBoxLayout(this);
auto *treeWidget = new QTreeWidget(this);
auto *item = new QTreeWidgetItem(treeWidget);
auto *button = new QPushButton(tr("Remove combo box"), this);
auto *comboBox = new QComboBox();
comboBox->addItems(QStringList() << "Bird" << "Fish" << "Ape");
treeWidget->setItemWidget(item, 0, comboBox);
l->addWidget(button);
l->addWidget(treeWidget);
connect(comboBox, &QComboBox::destroyed, [](){
qDebug("The combo box is gone.");
});
connect(button, &QPushButton::clicked, [treeWidget, item](){
treeWidget->removeItemWidget(item, 0);
});
resize(400, 300);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
结果
可以使用应用程序测试所描述的销毁小部件的方法
- 简单地关闭 window 会破坏树部件及其子组合框,因此会发出组合框的销毁信号并且 lambda 打印
The combo box is gone.
- 按下按钮后,调用连接到其
clicked
信号的 lambda 函数,从树形小部件中删除组合框。因为组合框也被(自动)删除,所以调用了第二个connect
语句的 lambda,它也打印了
The combo box is gone.