代码之家  ›  专栏  ›  技术社区  ›  Markus Meskanen

在固定列宽的QGridLayout中自动添加/删除小部件?

  •  0
  • Markus Meskanen  · 技术社区  · 6 年前

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

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

    看起来QGridLayout没有太多的特性,是我在这里遗漏了什么,还是我自己实现所有功能的唯一选择?或者我使用了错误的工具(QGridLayout)?

    0 回复  |  直到 6 年前
        1
  •  1
  •   Scheff's Cat    6 年前

    阿飞,没有自动重放 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();
    }
    

    testQDeleteFromLayoutShift.pro :

    SOURCES = testQDeleteFromLayoutShift.cc
    
    QT += widgets
    

    Qt Version: 5.13.0
    qGrid.parent().children().count(): 16
    qGrid.count(): 15
    

    Snapshot of testQDeleteFromLayoutShift

    点击按钮“Widget 8”后:

    Delete button "Widget 8"
    Destroyed: QPushButton(0x25521e3ef10) "Widget 8"
    qGrid.parent().children().count(): 15
    qGrid.count(): 14
    

    Snapshot of testQDeleteFromLayoutShift after clicking on button "Widget 8"

    Delete button "Widget 6"
    Destroyed: QPushButton(0x25521e3f7d0) "Widget 6"
    qGrid.parent().children().count(): 14
    qGrid.count(): 13
    

    Snapshot of testQDeleteFromLayoutShift after clicking on button "Widget 6"

    点击“小工具12”按钮后

    Delete button "Widget 12"
    Destroyed: QPushButton(0x25521e46ad0) "Widget 12"
    qGrid.parent().children().count(): 13
    qGrid.count(): 12
    

    Snapshot of testQDeleteFromLayoutShift after clicking on button "Widget 12"

    笔记:

    • 我用lambdas作为 QPushButton::clicked() QGridLayout QPushButton* 给处理者 deleteFromGrid()

    • 通过一个 QTimer::singleShot() 删除FromGrid() 在的信号处理器中 直接来说,这会导致 Harakiri 因为最后一行 : delete pQBtn; .


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

    SO: qgridlayout add and remove sub layouts

    可能会很有趣。