【问题标题】:Automatically add/remove widgets in QGridLayout with fixed column width?在具有固定列宽的 QGridLayout 中自动添加/删除小部件?
【发布时间】:2020-04-16 16:54:09
【问题描述】:

假设我正在创建一个包含 3 列的图片库视图(即每行显示 3 张图片)。我可以使用除法和余数将新图像添加到末尾:

int row = images.size() / 3;
int col = images.size() % 3;
gridLayout->addWidget(myImage, row, col);  

当我想从中间删除图像时出现问题;它现在只留下两张图片的中间行,而不是“移动”所有内容并用 3 张图片填充每一行(最后一行除外)。

似乎 QGridLayout 没有太多功能,我在这里遗漏了什么,还是我自己实现所有东西的唯一选择?还是我一开始就使用了错误的工具(QGridLayout)?

【问题讨论】:

    标签: c++ qt gridview layout qgridlayout


    【解决方案1】:

    AFAIK,QGridLayout 中没有自动重新布局。我什至不确定QGridLayout 是否用于此目的。

    恕我直言,QTableViewQTableWidget 可能是更好的选择。
    (关于这个,我想到了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() 的信号处理程序,将相关的 QGridLayoutQPushButton* 传递给处理程序 deleteFromGrid() - 恕我直言,这是最方便的方式。

    • deleteFromGrid() 是通过延迟为 0 的 QTimer::singleShot() 调用的。如果我直接在 QPushButton::clicked() 的信号处理程序中调用 deleteFromGrid(),这将导致某种 Harakiri 由于deleteFromGrid() 中的最后一行:delete pQBtn;
      嵌套的 lambdas 可能看起来有点吓人,抱歉。


    在写这个答案时,我想起了我的一个老朋友:

    SO: qgridlayout add and remove sub layouts

    可能会感兴趣。

    【讨论】:

      猜你喜欢
      • 2018-05-03
      • 2011-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-28
      • 2019-12-09
      • 1970-01-01
      相关资源
      最近更新 更多