重用与项目关联的窗口小部件时QListWidget崩溃

时间:2019-07-15 17:57:01

标签: c++ qt

我有一个继承自QWidget的自定义简单小部件,并将其添加到QListWidget中是这样的:

void MainWindow::AddToWidgetList(const QString &tag, const QString &html)
{
    HtmlItem *html_item = new HtmlItem();
    html_item->set_tag(tag);
    html_item->set_html(html);
    connect(html_item, SIGNAL(RemoveIt(uintptr_t)), this, SLOT(on_RmBtn_clicked(uintptr_t)));
    QListWidgetItem *list_item = new QListWidgetItem();
    html_item->set_list_item(list_item);
    list_item->setSizeHint(html_item->sizeHint());
    ui->CodeBlocks->addItem(list_item);
    ui->CodeBlocks->setItemWidget(list_item, html_item);
}

然后我想在按下按钮时向上移动所选元素

void MainWindow::on_UpArrowBtn_clicked()
{
    if (ui->CodeBlocks->count() < 2)
        return;
    int current_row = ui->CodeBlocks->currentRow();
    if (current_row == 0)
        return;
    HtmlItem *item_widget = (HtmlItem*)ui->CodeBlocks->itemWidget(ui->CodeBlocks->item(current_row));
    QListWidgetItem *item = ui->CodeBlocks->takeItem(current_row);
    ui->CodeBlocks->insertItem(current_row - 1, item);
    ui->CodeBlocks->setItemWidget(item, item_widget);
}

但我在此行崩溃:

ui->CodeBlocks->setItemWidget(item, item_widget);

1 个答案:

答案 0 :(得分:1)

以下示例显示了正在发生的情况。基本上,规则是这样的:

调用setItemWidget将Item-Widget的所有权转移到QListWidget实例。因此,QListWidget有责任销毁已设置的Item-Widget。

现在,QListWidget没有成员,因此可以撤回已设置的Item-Widget的所有权。唯一的选择是创建一个具有与要删除的Item-Widget相同属性的新Item-Widget。

请注意,Item-Widget ist稍后会在返回事件循环后删除,这是通过在deleteLater()内部调用takeItem来完成的。因此,访问label直到该插槽的末尾都是有效的。

如果您对此行为不满意,仍然可以使用自己的委托切换到QListView类。尽管这似乎需要更多工作,但它是可扩展的方法。

#include <QApplication>
#include <QHBoxLayout>
#include <QPushButton>
#include <QListWidget>
#include <QLabel>
#include <QDebug>

int main(int argc, char** args) {
    QApplication app(argc, args);
    auto frame = new QFrame;
    auto listWidget = new QListWidget;
    for (auto iter=0; iter<10; iter++)
    {
        auto label = new QLabel(QString("Item-%1").arg(iter));
        auto item = new QListWidgetItem();      
        listWidget->addItem(item);
        listWidget->setItemWidget(item, label); // listWidget becomes the owner of label
    }
    auto moveUp = new QPushButton("Move Up");
    frame->setLayout(new QHBoxLayout);
    frame->layout()->addWidget(listWidget);
    frame->layout()->addWidget(moveUp);
    frame->show();
    QObject::connect(moveUp, &QPushButton::clicked, [&]()
    {
            auto row = listWidget->currentRow();
            auto item=listWidget->currentItem();
            if (!item) return;
            if (row == 0) return;

            auto label = qobject_cast<QLabel*>(listWidget->itemWidget(item));
            if (!label) return;

            QObject::connect(label, &QLabel::destroyed, []()
                {
                    qDebug() << "Destroyed"; // takeItem calls deleteLater on itemWidget
                });
            auto myItem=listWidget->takeItem(row);
            listWidget->insertItem(row-1,myItem);
            listWidget->setItemWidget(item, new QLabel(label->text())); // copy content of itemWidget and create new widget
            listWidget->setCurrentRow(row-1);
    });
    app.exec();
}