2
votes

(Using Qt 4.6.3, x64, linux)

I'm testing how to properly insert widgets into a existing a QGridLayout filled with various widgets. A broken down contrived case is the following:

QApplication app(argc,argv);
QWidget      w;
QGridLayout* gl     = new QGridLayout(&w);
QLabel*      label  = new QLabel("Image Size:");
QLineEdit*   wedit  = new QLineEdit("100");
QLabel*      xlabel = new QLabel("x");

wedit->setAlignment(Qt::AlignRight);
gl->addWidget(label);
gl->addWidget(xlabel, 0, 1, 1, 1);
gl->addWidget(wedit,  0, gl->columnCount());

Which creates the following widget:

Valid XHTML.

Assuming that have an existing QGridLayout as above, but without the "x" label, and I wished to insert this into the layout, switching the latter two addWidget lines might seem valid, i.e.:

\\ same as above
gl->addWidget(label);
gl->addWidget(wedit,  0, gl->columnCount());
gl->addWidget(xlabel, 0, 1, 1, 1);

This however, creates the following:

Valid XHTML

The gl->columnCount() after this is still 2, as both the x-label and the QLineEdit are filling the same cell. Based on this knowledge, the following code produces the initial desired result:

gl->addWidget(label);
gl->addWidget(wedit,  0, 2); // note: specified column 2, columnCount() is now 3
gl->addWidget(xlabel, 0, 1, 1, 1);

Though this is not particularly useful, as the original layout in question isn't built with later layouts in mind.

Since addWidget allows for specifying cell position, as well as row/column span, it seems odd that Qt wouldn't automatically replace the existing widgets in the layout. Does anyone have a suggestion as to how I might overcome this? I assume it would be possible to recreate a QGridLayout and copy into it the children of the original, taking care to insert the additional widget in the right location. This however is ugly, and susceptible to Qt version issues (as I want to modify a built in widget).

Edit:

I realize that I'm making the assumption of thinking in a QHBoxLayout way, where inserting a widget is uniquely understood, whereas in a QGridLayout this isn't the case (?).

I can clarify that I ultimately would like to modify QFileDialog::getSaveFileName, by inserting a widget (similar to the widget shown above) right above the two lower rows (i.e. above "File &Name:").

Thanks

2
If there is a way to change the row and column of child widgets in the QGridLayout, it might be possible to make a function that takes in a rowIndex, and then shifts the row of every child widget in row rowIndex and higher (i.e. further down).swalog
QFileDialog::*, i believe, use the native file dialogs if available. So i don't think you are able to modify their layout using Qt.RedX
I've actually worked out a solution to my problem, so I believe it is possible. I'm still having minor issues with the initial focused child widget. I'll post a solution when I've sorted out the quirks and made it presentable for review.swalog

2 Answers

1
votes

Switching the latter two addWidget lines is not valid. For the following code:

gl->addWidget(label);
gl->addWidget(wedit,  0, gl->columnCount());
gl->addWidget(xlabel, 0, 1, 1, 1);

The arguments for the addWidget() calls are evaluated prior to adding the widget. Therefore, gl->columnCount() evaluates to one instead of two for the second call, since the column still has to be created. You are effectively adding two widgets to column one.

0
votes

A possible solution is to re-add the widgets that should be relocated. I.e.

QLayoutItem* x01 = gl->itemAtPosition(0,1);
gl->addWidget(x01->widget(), 0, 2);
gl->addWidget(xlabel, 0, 1, 1, 1);

Now, this isn't particularly pretty, or easy to maintain, as a new version of Qt might change the original widget, and blindly handpicking and relocating children isn't that clever. The following real example (the one I actually wanted to solve) was to alter the Qt's "Save As" dialog window, that shows up using QFileDialog::getSaveFileName.

class ImageFileDialog : public QFileDialog {
public:
  ImageFileDialog(QWidget* parent);
  ~ImageFileDialog();
  QString getFileName() const;
  QSize getImageSize() const;
  QDialog::DialogCode exec(); // Overriden

protected:
  void showEvent(QShowEvent* event); // Overriden

private:
  QString    fileName_;
  QSize      imageSize_;
  QLineEdit* widthLineEdit_;
  QLineEdit* heightLineEdit_;
};

And in the source (showing just constructor, focus handling and exec):

ImageFileDialog::ImageFileDialog(QWidget* parent)
  : fileName_(""),
    imageSize_(0,0),
    widthLineEdit_(0),
    heightLineEdit_(0)
{
  setAcceptMode(QFileDialog::AcceptSave);
  setFileMode(QFileDialog::AnyFile);
  setConfirmOverwrite(true);

  QGridLayout* mainLayout = dynamic_cast<QGridLayout*>(layout());
  assert(mainLayout->columnCount() == 3);
  assert(mainLayout->rowCount()    == 4);

  QWidget*     container      = new QWidget();
  QGridLayout* glayout        = new QGridLayout();
  QLabel*      imageSizeLabel = new QLabel("Image Size:");
  widthLineEdit_              = new QLineEdit("400");
  heightLineEdit_             = new QLineEdit("300");
  widthLineEdit_->setAlignment(Qt::AlignRight);
  heightLineEdit_->setAlignment(Qt::AlignRight);

  container->setLayout(glayout);
  glayout->setAlignment(Qt::AlignLeft);
  glayout->addWidget(widthLineEdit_);
  glayout->addWidget(new QLabel("x"), 0, 1);
  glayout->addWidget(heightLineEdit_, 0, 2);
  glayout->addWidget(new QLabel("[pixels]"), 0, 3);
  glayout->addItem(new QSpacerItem(250, 0), 0, 4);
  glayout->setContentsMargins(0,0,0,0); // Removes unwanted spacing

  // Shifting relevant child widgets one row down.
  int rowCount = mainLayout->rowCount();
  QLayoutItem* x00 = mainLayout->itemAtPosition(mainLayout->rowCount()-2,0);
  QLayoutItem* x10 = mainLayout->itemAtPosition(mainLayout->rowCount()-1,0);
  QLayoutItem* x01 = mainLayout->itemAtPosition(mainLayout->rowCount()-2,1);
  QLayoutItem* x11 = mainLayout->itemAtPosition(mainLayout->rowCount()-1,1);
  QLayoutItem* x02 = mainLayout->itemAtPosition(mainLayout->rowCount()-1,2);
  assert(x00);   assert(x01);   assert(x10);   assert(x11);   assert(x02);

  mainLayout->addWidget(x00->widget(), rowCount-1, 0, 1, 1);
  mainLayout->addWidget(x10->widget(), rowCount,   0, 1, 1);
  mainLayout->addWidget(x01->widget(), rowCount-1, 1, 1, 1);
  mainLayout->addWidget(x11->widget(), rowCount,   1, 1, 1);
  mainLayout->addWidget(x02->widget(), rowCount-1, 2, 2, 1);

  // Adding the widgets in the now empty row.
  rowCount        = mainLayout->rowCount();
  mainLayout->addWidget(imageSizeLabel, rowCount-3, 0, 1, 1 );
  mainLayout->addWidget(container,      rowCount-3, 1, 1, 1);

  // Setting the proper tab-order
  QLayoutItem* tmp  = mainLayout->itemAtPosition(mainLayout->rowCount()-2,1);
  QLayoutItem* tmp2 = mainLayout->itemAtPosition(mainLayout->rowCount()-1,1);
  assert(tmp); assert(tmp2);
  QWidget::setTabOrder(heightLineEdit_      , tmp->widget());
  QWidget::setTabOrder(tmp->widget(), tmp2->widget());
}

// Makes sure the right widget is in focus
void ImageFileDialog::showEvent(QShowEvent* event)
{
  widthLineEdit_->setFocus(Qt::OtherFocusReason);
}

// Called to create the widget
QDialog::DialogCode ImageFileDialog::exec()
{
  if (QFileDialog::exec() == QDialog::Rejected)
    return QDialog::Rejected;
  // The code that processes the widget form and stores results for later calls to
  // getImageSize()
  return QDialog:Accepted;
}

Which, using for instance

ImageFileDialog* dialog = new ImageFileDialog(&w);
dialog->exec();

Creates the following widget:

Valid XHTML


Comments and ways to do this better, or why this is just plain wrong are most welcome :)