QTableWidget 单元格小部件在交换后消失

QTableWidget cell widgets dissapear after swap

提问人:willy_j 提问时间:8/8/2023 更新时间:8/9/2023 访问量:48

问:

我有 3 列的 QTableWidget。第一列包含具有行名称的 QTableWidgetItem,另外两列包含两个单元格小部件。

enter image description here

我尝试使用以下代码交换表行:

connect(upButton, &QPushButton::clicked, this, [this, protectionWidget]()
    {
        int index = m_protectionWidgets->indexOf(protectionWidget);
        int upperIndex = index - 1;
        if (upperIndex >= 0)
        {
            QTableWidgetItem *nameItem = m_protectionsTable->takeItem(index, 0);
            ProtectionWidget *protectionWidgetCopy = protectionWidget;
            QWidget *buttons = m_protectionsTable->cellWidget(index, 2);

            QTableWidgetItem *upperNameItem = m_protectionsTable->takeItem(upperIndex, 0);
            ProtectionWidget *upperProtectionWidget = m_protectionWidgets->at(upperIndex);
            QWidget *upperButtons = m_protectionsTable->cellWidget(upperIndex, 2);

            m_protectionsTable->setItem(upperIndex, 0, nameItem);
            m_protectionsTable->setCellWidget(upperIndex, 1, protectionWidgetCopy);
            m_protectionsTable->setCellWidget(upperIndex, 2, buttons);

            m_protectionsTable->setItem(index, 0, upperNameItem);
            m_protectionsTable->setCellWidget(index, 1, upperProtectionWidget);
            m_protectionsTable->setCellWidget(index, 2, upperButtons);

            m_protectionWidgets->removeAt(index);
            m_protectionWidgets->insert(upperIndex, 1, protectionWidget);
        }
    });

但是当我单击upButton时,cellWidgets消失了。

enter image description here

怎么了?

qt qtablewidget

评论


答:

2赞 Atmo 8/9/2023 #1

setCellWidget 记录在您身上的情况是,如果不删除已经存在的小部件,您就无法在单元格上设置小部件。使用调试工具检测这是一件相当棘手的事情,因为删除是在下次执行事件循环时完成的,这要归功于对 的调用,在您的 lambda 已经完成执行很久之后。QObject::deleteLater

没有办法避免正在发生的删除,但是有一些方法可以使删除无害。我看到 2 种方式:

  1. 放置你的和里面的祭祀小部件。通过在正确的时间重置它们的父级,您可以实现在下一次调用 时仅删除空的祭祀小部件的效果。protectionWidgetbuttonssetCellWidget
  2. 与上面相同,但您完全停止调用,而只是交换小部件的父级。setCellWidget

下面的代码演示了你的错误(A和B),第一种方法(C和D)和第二种方法(E和F)。请注意,您需要考虑是否保留 lambda 和/或保持原样,或者将它们转换为受保护/私有方法。createWidgetextractButtonswapParent

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    QTableWidget t;
    t.setRowCount(6);
    t.setColumnCount(2);

    t.setItem(0, 0, new QTableWidgetItem("Original: A"));
    t.setItem(1, 0, new QTableWidgetItem("Original: B"));
    t.setItem(2, 0, new QTableWidgetItem("Method 1: C"));
    t.setItem(3, 0, new QTableWidgetItem("Method 1: D"));
    t.setItem(4, 0, new QTableWidgetItem("Method 2: E"));
    t.setItem(5, 0, new QTableWidgetItem("Method 2: F"));

    auto createWidget = [](QPushButton* button) -> QWidget*
    {
        QWidget* result = new QWidget();
        QLayout* l = new QVBoxLayout(result);
        l->setContentsMargins(QMargins());
        l->addWidget(button);
        button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
        return result;
    };
    auto extractButton = [] (QWidget* widget) -> QPushButton*
    {
        QPushButton* result = widget->findChild<QPushButton*>();
        result->setParent(nullptr);
        return result;
    };
    auto swapParent = [](QWidget* from, QWidget* to) -> void
    {
        auto fromChild = from->findChild<QWidget*>(), toChild = to->findChild<QWidget*>();
        from->layout()->removeWidget(fromChild);
        to->layout()->removeWidget(toChild);
        fromChild->setParent(to);
        toChild->setParent(from);
        from->layout()->addWidget(toChild);
        to->layout()->addWidget(fromChild);
    };

    auto bA = new QPushButton("A"), bB = new QPushButton("B"),
         bC = new QPushButton("C"), bD = new QPushButton("D"),
         bE = new QPushButton("E"), bF = new QPushButton("F");
    QObject::connect(bA, &QObject::destroyed, []() { qDebug() << "Button A destroyed "; });
    QObject::connect(bB, &QObject::destroyed, []() { qDebug() << "Button B destroyed "; });
    QObject::connect(bC, &QObject::destroyed, []() { qDebug() << "Button C destroyed "; });
    QObject::connect(bD, &QObject::destroyed, []() { qDebug() << "Button D destroyed "; });
    QObject::connect(bE, &QObject::destroyed, []() { qDebug() << "Button E destroyed "; });
    QObject::connect(bF, &QObject::destroyed, []() { qDebug() << "Button F destroyed "; });

    t.setCellWidget(0, 1, bA);
    t.setCellWidget(1, 1, bB);
    t.setCellWidget(2, 1, createWidget(bC));
    t.setCellWidget(3, 1, createWidget(bD));
    t.setCellWidget(4, 1, createWidget(bE));
    t.setCellWidget(5, 1, createWidget(bF));
    //End of the initialization.
    //The next lines simulate the lambdas connected to the button signals with:
    // - what not do to: swap A<->B, unprotected
    // - method 1: swap C<->D, each protected by a sacrifical widget
    // - method 2: reparent E and F instead of literally swapping them
    auto b0 = t.cellWidget(0, 1), b1 = t.cellWidget(1, 1),
         b2 = t.cellWidget(2, 1), b3 = t.cellWidget(3, 1),
         b4 = t.cellWidget(4, 1), b5 = t.cellWidget(5, 1);
    //b0 == bA && b1 == bB && b2 == bC && b3 == bD && b4 == bE && b5 == bF

    //Incorrect for A and B (see the qDebug messages)
    t.setCellWidget(0, 1, b1);
    t.setCellWidget(1, 1, b0);

    //Method 1
    t.setCellWidget(2, 1, createWidget(extractButton(b3)));
    t.setCellWidget(3, 1, createWidget(extractButton(b2)));
    //Method 2
    swapParent(b4, b5);

    // The next 2 lines are only to exit cleanly.
    t.setCellWidget(0, 1, nullptr);
    t.setCellWidget(1, 1, nullptr);

    t.show();
    return app.exec();
}

不言而喻,但这个演示确实需要大量的指针检查。