Add widgets to QFileDialog - c++

I need to add a widget (QTableWidget) into QFileDialog's layout. I know that it is QGridLayout with sizes (3,4). The table must be in 3-rd row and span all columns.
QTableWidget* tableWidget = new QTableWidget(this);
QGridLayout *layout = static_cast<QGridLayout*>(QFileDialog::layout());
layout->addWidget(tableWidget, 2, 0, 1, 4);
With this code the original 3-rd row which contains lineEdit and save/open pushButton disappears. How can I add widgets between already existing widgets of QGridLayout so that original widgets remain in the layout.

I strongly recommend you not to rely on QFileDialog's implementation. The layout can be different on different platforms or different versions of Qt. It may be more correct to place your table under the dialog or to the right of it. This can be done easily without altering the layout of the QFileDialog itself. Just create a QVBoxLayout and put QFileDialog and QTableWidget inside it.
However, the question has been asked, and the solution exists. QGridLayout has no functionality such as QBoxLayout::insertItem. So we need to implement this behavior manually. The plan is:
Obtain the list of layout items placed in 3rd and 4th rows.
Calculate new positions of items.
Take elements out of item and add them back at new positions.
Working code:
QFileDialog* f = new QFileDialog();
f->setOption(QFileDialog::DontUseNativeDialog, true); //we need qt layout
QGridLayout *layout = static_cast<QGridLayout*>(f->layout());
QList< QPair<QLayoutItem*, QList<int> > > moved_items;
f->show();
for(int i = 0; i < layout->count(); i++) {
int row, column, rowSpan, columnSpan;
layout->getItemPosition(i, &row, &column, &rowSpan, &columnSpan);
if (row >= 2) {
QList<int> list;
list << (row + 1) << column << rowSpan << columnSpan;
moved_items << qMakePair(layout->takeAt(i), list);
i--; // takeAt has shifted the rest items
}
}
for(int i = 0; i < moved_items.count(); i++) {
layout->addItem(moved_items[i].first,
moved_items[i].second[0],
moved_items[i].second[1],
moved_items[i].second[2],
moved_items[i].second[3]);
}
QTableWidget* tableWidget = new QTableWidget();
layout->addWidget(tableWidget, 2, 0, 1, 4);

Related

QTableView bigger than its container

I am working on Qt applicaction. There I have QMainWindow. Inside it I have added QTableView. When I run the application I see that I need to scroll to display the whole table and also blank space shows up below it.
I would like main window to resize horizontally in order to use space needed by the table. Also I would like it to resize vertically to not having space unused. How could I achieve that?
This is my code so far:
void MainWindow::initUi() {
setWindowTitle(tr("Main Window"));
QWidget* centralWidget = new QWidget(this);
QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);
QFormLayout *upperLayout = new QFormLayout;
// Default layout appearance of QMacStyle
upperLayout->setRowWrapPolicy(QFormLayout::DontWrapRows);
upperLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
upperLayout->setFormAlignment(Qt::AlignHCenter | Qt::AlignTop);
upperLayout->setLabelAlignment(Qt::AlignLeft);
QVBoxLayout *resultsLayout = new QVBoxLayout;
QTableView* table = new QTableView(centralWidget);
table->verticalHeader()->hide();
QStandardItemModel* model= new QStandardItemModel(4, 4);
for (int row = 0; row < 4; ++row) {
for (int column = 0; column < 4; ++column) {
QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
model->setItem(row, column, item);
}
}
table->setModel(model);
QLabel* upperLabel = new QLabel(tr("Label:"), centralWidget);
upperLabel->setAlignment(Qt::AlignLeft);
resultLabel = new QLabel(tr("Result goes here"), centralWidget);
mainLayout->addLayout(resultsLayout);
resultsLayout->addLayout(upperLayout);
resultsLayout->addWidget(table);
upperLayout->addRow(upperLabel, resultLabel);
centralWidget->setLayout(mainLayout);
setCentralWidget(centralWidget);
this->adjustSize();
}
Set the sizeAdjustPolicy of the table to AdjustToContents view, then set the size policy to Fixed in both horizontal and vertical directions.
AdjustToContents might incur a slight performance penalty for dynamic contents in the view, since every data change may change the layout.
The Qt Designer is a really nifty tool to figure layout issues out quickly; the {table,list,tree} widgets behave exactly the same as the views do (because they're the same) and the widgets can be quickly filled with dummy data in Qt Designer.

How to sort a list of QWidgets in QScrollArea

I have a number of rows with images, text, and a bunch of data. Each row is a separate QWidget. Each has a unique number assigned to it (input by user, used setObjectName("rzad_"+number) ). What I need to do is sort the rows in descending order every time a new is added.
How can I do that?
edit:
I've tried a global QListWidget, but it crashes when retrieving the widget. Right after adding the QWidget to the QListWidget, I can do this:
ui->scrollAreaWidgetContentsZdjeciaButow->layout()->addWidget(listaWidget->findChild<QWidget*>("rzadKontener_" + poziom));
It will add the widget to the area, but after that, when I call sortRows method,
std::sort(poziomyNachylenia.begin(), poziomyNachylenia.end(), std::greater<int>());
QWidget* nowyZestawRzedow = new QWidget; //new widget for sorted rows
QVBoxLayout* nowyZestawRzedowLayout = new QVBoxLayout;
nowyZestawRzedowLayout->setContentsMargins(-5, -5, -5, -5);
nowyZestawRzedowLayout->setAlignment(Qt::AlignLeft);
nowyZestawRzedowLayout->setAlignment(Qt::AlignTop);
nowyZestawRzedow->setLayout(nowyZestawRzedowLayout);
qDebug() << ui->scrollAreaWidgetContentsZdjeciaButow->layout()->count() << "count";
for (int i = 0; i < ui->scrollAreaWidgetContentsZdjeciaButow->layout()->count(); ++i) {
QString numerRzedu = QString::number(poziomyNachylenia[i]);
//this crashes the app
nowyZestawRzedow->layout()->addWidget(listaWidget->findChild<QWidget*>("rzadKontener_" + numerRzedu));
//and so does this.
QString nameT = listaWidget->findChild<QWidget*>("rzadKontener_" + numerRzedu)->objectName();
qDebug() << "added" << nameT;
}
What gives?

Qt C++ - QListWidget layout doesn't scale to fit the content

I have a QWidget rzadKontener that represents a row in a QListWidget.
QWidget* rzadKontener = new QWidget;
QHBoxLayout* rzadKontenerLayout = new QHBoxLayout();
rzadKontenerLayout->setAlignment(Qt::AlignLeft);
rzadKontenerLayout->setAlignment(Qt::AlignTop);
rzadKontener->setObjectName("rzadKontener_" + poziom);
rzadKontener->setFixedHeight(200);
rzadKontener->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
rzadKontener->setLayout(rzadKontenerLayout);
(....)
QListWidgetItem* newItemRzad = new QListWidgetItem;
newItemRzad->setSizeHint(QSize(listSize*225, 200));
QString poziomText = poziom;
newItemRzad->setText(poziomText);
newItemRzad->setTextColor(QColor(Qt::white));
ui->listWidgetZdjeciaModelu->addItem(newItemRzad);
ui->listWidgetZdjeciaModelu->setItemWidget(newItemRzad, rzadKontener);
It contains a number of items, that are QWidgets with pictures, buttons and text. It is then placed in a QListWidget as a row full of these items. When I add 5 items at once, while creating a new rzadKontener, scroll bars in the QListWidget will appear. But if I add 3 and then 2 items later on, they'll go out of bounds without a scrollbar. How can I force the layout to scale to the new rzadKontener's size?
I found an answer. Instead of modifying the QWidget inside the list, I should be modifying the QListWidgetItem it's inside of (the parent...).
QWidget* newPicture = new QWidget;
yadda yadda yadda (....)
int currentRowWidth = ui->listWidgetZdjeciaModelu->findChild<QWidget*>(objectName)->width(); //gets max width of rzadKontener, which fills the entirety of the row - it equals the QListWidgetItem's width
int newWidth = currentRowWidth + 225; //225 is a fixed width of newPicture
ui->listWidgetZdjeciaModelu->item(0)->setSizeHint(QSize(newWidth, 200));
ui->listWidgetZdjeciaModelu->findChild<QWidget*>(objectName)->layout()->addWidget(newPicture );
We can do ui->listWidgetZdjeciaModelu->item(0)->setSizeHint(QSize(newWidth, 200)); instead of a specific row (->item(row)), because the list is a rectangle. Doesn't matter which row we enlarge, the entire thing will stretch anyway.
But, if you want to get the row number, you can do it this way:
int row=0;
//we make a list. Each of my QListWidgetItem has a unique string poziom in it, so I can filter by that.
QList<QListWidgetItem *> items = ui->listWidgetZdjeciaModelu->findItems(poziom, Qt::MatchContains);
if (items.size() > 0) {
//we use the first (and only, in my case) item on this list to get row number
row = ui->listWidgetZdjeciaModelu->row(items[0]);
}
Weird thing is, I already tried this. Must've kept making a typo.

QTableWidget filled with QLineEdits does not fire signals

I'm relatively new to QT. In my code, I create a QTableWidget, iterate through the rows and set the cells to QLineEdits and QCheckBoxes. I want to make it so that changing the text within any of the QLineEdits or checking/unchecking the QCheckBoxes causes my table to fire a signal passing either the item in question, or the row/column that it's within.
I build the table here:
for(int row=0; row < conditionTable->rowCount(); row++)
{
QLineEdit *condition = new QLineEdit;
conditionTable->setCellWidget(row, 0, condition);
QLineEdit *minBoundField = new QLineEdit;
conditionTable->setCellWidget(row, 1, minBoundField);
QLineEdit *maxBoundField = new QLineEdit;
conditionTable->setCellWidget(row, 2, maxBoundField);
QCheckBox *checkbox = new QCheckBox;
conditionTable->setCellWidget(row, 3, checkbox);
if(row > 0)
{
condition->setReadOnly(true);
minBoundField->setReadOnly(true);
maxBoundField->setReadOnly(true);
checkbox->setCheckable(false);
}
}
I then try to make it so that changes to the table can be handled by one of the slot methods:
connect(conditionTable, SIGNAL(itemChanged(QTableWidgetItem*)),
this, SLOT(handleConditionTableChange(QTableWidgetItem*)));
However, this doesn't seem to work, and I'm not sure where to go from here. Any help would be appreciated.
You shouldn't be using QLineEdit and QCheckBox here.
To add a check box to your QTableWidget do the following:
QTableWidgetItem* item = new QTableWidgetItem("check box");
item->setFlags(Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Unchecked);
tableWidget->setItem(row, column, item);
To add an line edit:
QTableWidgetItem* item = new QTableWidgetItem("line edit");
tableWidget->setItem(row, column, item);
With this setup, the signal will be emitted when an item is changed.
Edit:
For your example, try something like:
for(int row=0; row < conditionTable->rowCount(); row++)
{
QTableWidgetItem* condition = new QTableWidgetItem("");
conditionTable->setItem(row, 0, condition);
QTableWidgetItem *minBoundField = new QTableWidgetItem("");
conditionTable->setItem(row, 1, minBoundField);
QTableWidgetItem *maxBoundField = new QTableWidgetItem("");
conditionTable->setItem(row, 2, maxBoundField);
QTableWidgetItem *checkbox = new QTableWidgetItem("");
checkbox->setFlags(Qt::ItemIsUserCheckable);
checkbox->setCheckState(Qt::Unchecked);
conditionTable->setItem(row, 3, checkbox);
if(row > 0)
{
condition->setFlags(Qt::NoItemFlags);
minBoundField->setFlags(Qt::NoItemFlags);
maxBoundField->setFlags(Qt::NoItemFlags);
checkbox->setFlags(Qt::NoItemFlags);
}
}
If you still want to use QLineEdit and QCheckBox for some reason, you will need to connect each line edit and each check box to a slot.

Delete A Row From QGridLayout

All, I am maintaining a QGridLayout of QLabels which show the coefficients of a polynomial. I represent my polynomial using QList<double>.
Each time I update my coefficients, I update my labels. When changing the size of the list, my method does not works well. QGridLayout::rowCount() doesn't update correctly. I am wondering if there's a way to remove rows from a QGridLayout.
Code follows, updating the QGridLayout size with more (or less) QLabels
int count = coefficients->count(); //coefficients is a QList<double> *
if(count != (m_informational->rowCount() - 1)) //m_information is a QGridLayout
{
SetFitMethod(0);
for(int i = 0; i < count; ++i)
{
QLabel * new_coeff = new QLabel(this);
new_coeff->setAlignment(Qt::AlignRight);
m_informational->addWidget(new_coeff, i+1, 0);
QLabel * param = new QLabel(this);
param->setAlignment(Qt::AlignLeft);
param->setText(QString("<b><i>x</i><sup>%2</sup></b>").arg(count-i-1));
m_informational->addWidget(param, i+1, 1);
QSpacerItem * space = new QSpacerItem(0,0,QSizePolicy::Expanding);
m_informational->addItem(space, i+1, 1);
}
m_informational->setColumnStretch(0, 3);
m_informational->setColumnStretch(1, 1);
m_informational->setColumnStretch(2, 1);
}
The SetFitMethod (it's an initial mockup)
void SetFitMethod(int method)
{
ClearInformational();
switch(method)
{
case 0: //Polynomial fit
QLabel * title = new QLabel(this);
title->setText("<b> <u> Coefficients </u> </b>");
title->setAlignment(Qt::AlignHCenter);
m_informational->addWidget(title,0,0,1,3, Qt::AlignHCenter);
}
}
The Clearing Method:
void ClearInformational()
{
while(m_informational->count())
{
QLayoutItem * cur_item = m_informational->takeAt(0);
if(cur_item->widget())
delete cur_item->widget();
delete cur_item;
}
}
The issue is that QGridLayout::rowCount() doesn't actually return the number of rows that you can see, it actually returns the number of rows that QGridLayout has internally allocated for rows of data (yes, this isn't very obvious and isn't documented).
To get around this you can either delete the QGridLayout and recreate it, or if you're convinced that your column count won't change, you can do something like this:
int rowCount = m_informational->count()/m_informational->columnCount();
I solved this by creating a QVBoxLayout (for rows) and within this I was adding QHBoxLayout (for columns). In the QHBoxLayout I was then inserting my widgets (in one row). This way I was able to nicely remove rows - overall row count was working as it should be. Additionally to this I got also an insert method, thanks to which I was able to insert new rows into specific locations (everything was correctly reordered/renumbered).
Example (only from head):
QVBoxLayout *vBox= new QVBoxLayout(this);
//creating row 1
QHBoxLayout *row1 = new QHBoxLayout();
QPushButton *btn1x1 = new QPushButton("1x1");
QPushButton *btn1x2 = new QPushButton("1x2");
row1->addWidget(btn1x1);
row1->addWidget(btn1x2);
//adding to vBox - here you can use also insertLayout() for insert to specific location
vBox->addlayout(row1);
//creating row 2
QHBoxLayout *row2 = new QHBoxLayout();
QPushButton *btn2x1 = new QPushButton("2x1");
QPushButton *btn2x2 = new QPushButton("2x2");
row2->addWidget(btn2x1);
row2->addWidget(btn2x2);
//adding to vBox - here you can use also insertLayout() for insert to specific location
vBox->addlayout(row2);
Well, my solution was to also delete the QGridLayout in ClearInformational