AFAIK,QGridLayout 中没有自动重新布局。我什至不确定QGridLayout 是否用于此目的。
恕我直言,QTableView 或 QTableWidget 可能是更好的选择。
(关于这个,我想到了QAbstractItemModel::moveRows()。)
但是,这并不意味着无法实现。
我制作了一个 MCVE 来证明这一点 – testQDeleteFromLayoutShift.cc:
#include <cassert>
#include <vector>
#include <QtWidgets>
// comment out to get rid of console diagnostic output
#define DIAGNOSTICS
class PushButton: public QPushButton {
public:
PushButton(const QString &text, QWidget *pQParent = nullptr):
QPushButton(text)
{ }
#ifdef DIAGNOSTICS
virtual ~PushButton() { qDebug() << "Destroyed:" << this << text(); }
#else // (not) DIAGNOSTICS
virtual ~PushButton() = default;
#endif // DIAGNOSTICS
};
// number of columns in grid
const int wGrid = 3;
// fill grid with a certain amount of buttons
std::vector<QPushButton*> fillGrid(QGridLayout &qGrid)
{
const int hGrid = 5;
std::vector<QPushButton*> pQBtns; pQBtns.reserve(wGrid * hGrid);
unsigned id = 0;
for (int row = 0; row < hGrid; ++row) {
for (int col = 0; col < wGrid; ++col) {
QPushButton *pQBtn = new PushButton(QString("Widget %1").arg(++id));
qGrid.addWidget(pQBtn, row, col);
pQBtns.push_back(pQBtn);
}
}
#ifdef DIAGNOSTICS
qDebug() << "qGrid.parent().children().count():"
<< dynamic_cast<QWidget*>(qGrid.parent())->children().count();
qDebug() << "qGrid.count():"
<< qGrid.count();
#endif // DIAGNOSTICS
return pQBtns;
}
// delete a button from grid (shifting the following)
void deleteFromGrid(QGridLayout &qGrid, QPushButton *pQBtn)
{
qDebug() << "Delete button" << pQBtn->text();
// find index of widget in grid
int i = 0;
const int n = qGrid.count();
while (i < n && qGrid.itemAt(i)->widget() != pQBtn) ++i;
assert(i < n);
// find item position in grid
int row = -1, col = -1, rowSpan = 0, colSpan = 0;
qGrid.getItemPosition(i, &row, &col, &rowSpan, &colSpan);
// remove button from layout
QLayoutItem *pQItemBtn = qGrid.itemAt(i);
qGrid.removeItem(pQItemBtn);
// reposition all following button layouts
for (int j = i + 1; j < n; ++j) {
QLayoutItem *pQItem = qGrid.takeAt(i);
const int row = (j - 1) / wGrid, col = (j - 1) % wGrid;
qGrid.addItem(pQItem, row, col);
}
delete pQBtn;
#if 1 // diagnostics
qDebug() << "qGrid.parent().children().count():"
<< dynamic_cast<QWidget*>(qGrid.parent())->children().count();
qDebug() << "qGrid.count():"
<< qGrid.count();
#endif // 0
}
// application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
QWidget qWin;
qWin.setWindowTitle(QString::fromUtf8("Demo Delete from QGridLayout (with shift)"));
QGridLayout qGrid;
qWin.setLayout(&qGrid);
std::vector<QPushButton*> pQBtns = fillGrid(qGrid);
qWin.show();
// install signal handlers
for (QPushButton *const pQBtn : pQBtns) {
QObject::connect(pQBtn, &QPushButton::clicked,
[&qGrid, pQBtn](bool) {
QTimer::singleShot(0, [&qGrid, pQBtn]() {
deleteFromGrid(qGrid, pQBtn);
});
});
}
// runtime loop
return app.exec();
}
一个用于构建它的 Qt 项目文件 – testQDeleteFromLayoutShift.pro:
SOURCES = testQDeleteFromLayoutShift.cc
QT += widgets
Windows 10 中的输出(使用 VS2017 构建):
Qt Version: 5.13.0
qGrid.parent().children().count(): 16
qGrid.count(): 15
点击“Widget 8”按钮后:
Delete button "Widget 8"
Destroyed: QPushButton(0x25521e3ef10) "Widget 8"
qGrid.parent().children().count(): 15
qGrid.count(): 14
点击“Widget 6”按钮后
Delete button "Widget 6"
Destroyed: QPushButton(0x25521e3f7d0) "Widget 6"
qGrid.parent().children().count(): 14
qGrid.count(): 13
点击“Widget 12”按钮后
Delete button "Widget 12"
Destroyed: QPushButton(0x25521e46ad0) "Widget 12"
qGrid.parent().children().count(): 13
qGrid.count(): 12
注意事项:
我将 lambdas 用于 QPushButton::clicked() 的信号处理程序,将相关的 QGridLayout 和 QPushButton* 传递给处理程序 deleteFromGrid() - 恕我直言,这是最方便的方式。
deleteFromGrid() 是通过延迟为 0 的 QTimer::singleShot() 调用的。如果我直接在 QPushButton::clicked() 的信号处理程序中调用 deleteFromGrid(),这将导致某种 Harakiri 由于deleteFromGrid() 中的最后一行:delete pQBtn;。
嵌套的 lambdas 可能看起来有点吓人,抱歉。
在写这个答案时,我想起了我的一个老朋友:
SO: qgridlayout add and remove sub layouts
可能会感兴趣。