QTableView bigger than its container - c++

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.

Related

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.

QT GridLayout add Stacked QLabel

I am creating an image gallery, i've implemented the reading in of Files and showing them in a resizable scroll-Area. We've decided to add meta-tags / Buttons and i am searching for a convenient way not to change too much but add this little features.
Any suggestion how i can achieve this? Can i add two Qlabels to each other? I tried to stuck two labels in a new layout and push this to the scrollWidgetLayout, but then i have only one Thumbnail.
//Create new ThumbNail-Object
thumbNail = new Thumbnail(ui->scrollArea);
scrollWidgetLayout->addWidget(thumbNail);
In the picture you can see what i have already and what i need (yellow).
You create a widget that acts like a container and put the labels inside it. Set a layout to this widget, I used QVBoxLayout. A better design would be to create a custom widget by subclassing QWidget, but I just used QFrame to make the example quick and simple.
centralWidget()->setLayout(new QVBoxLayout);
QScrollArea *area = new QScrollArea(this);
area->setWidgetResizable(true);
area->setWidget(new QWidget);
QGridLayout *grid = new QGridLayout;
area->widget()->setLayout(grid);
centralWidget()->layout()->addWidget(area);
for(int row = 0; row < 2; row++)
{
for(int column = 0; column < 5; column++)
{
QFrame *container = new QFrame; // this is your widget.. you can also subclass QWidget to make a custom widget.. might be better design
container->setStyleSheet("QFrame{border: 1px solid black;}"); // just to see the shapes better.. you don't need this
container->setLayout(new QVBoxLayout); // a layout for your widget.. again, if you subclass QWidget do this in its constructor
container->layout()->addWidget(new QLabel("TOP")); // the top label.. in your case where you show the icon
container->layout()->addWidget(new QLabel("BOTTOM")); // the bottom label.. in your case where you show the tag
grid->addWidget(container, row, column); // add the widget to the grid
}
}

Add widgets to QFileDialog

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);

Qt Scroll Area does not add in scroll bars

Hi guys I have to dynamically create push buttons depending on user inputs, therefore if user gives a large input number the widget containing the push buttons has to have the ability to scroll up and down. For this reason I am using QScrollArea. I generate the template in Qt designer and the UIC generates the code for me after which I add in my part which should handle dynamic creation of push buttons. However, I can not seem to get the vertical scroll bars to appear. Here is the relevant part of the code.
verticalWidget = new QWidget(FWHMWorkflowDialog);
verticalWidget->setObjectName(QString::fromUtf8("verticalWidget"));
verticalWidget->setMinimumSize(QSize(150, 0));
verticalWidget->setMaximumSize(QSize(150, 16777215));
verticalLayout_5 = new QVBoxLayout(verticalWidget);
verticalLayout_5->setObjectName(QString::fromUtf8("verticalLayout_5"));
scrollArea = new QScrollArea(verticalWidget);
scrollArea->setObjectName(QString::fromUtf8("scrollArea"));
scrollArea->setMaximumSize(QSize(150, 16777215));
scrollArea->setWidgetResizable(true);
scrollAreaWidgetContents = new QWidget();
scrollAreaWidgetContents->setObjectName(QString::fromUtf8("scrollAreaWidgetContents"));
scrollAreaWidgetContents->setGeometry(QRect(0, 0, 130, 432));
numberOfSlices = numberSlices;
for (int i = 0; i < numberOfSlices; i++)
{
QWidget *horizontalWidget = new QWidget(scrollAreaWidgetContents);
horizontalWidget->setMaximumSize(150,40);
horizontalWidget->setGeometry(QRect(0, i*40, 150, 40));
hWidgetList.push_back(horizontalWidget);
QHBoxLayout *hLayout = new QHBoxLayout(horizontalWidget);
hLayoutList.push_back(hLayout);
hLayout->setSizeConstraint(QLayout::SetMinimumSize);
hLayout->setContentsMargins(-1, 1, -1, 1);
QPushButton *pushButton = new QPushButton(horizontalWidget);
pushButtonList.push_back(pushButton);
QString temp = QString("m_sliceButton").arg(i);
pushButtonList[i]->setObjectName(temp);
pushButtonList[i]->setGeometry(QRect(10, 20+i*40, 98, 27));
hLayout->addWidget(pushButton);
QCheckBox *checkBox = new QCheckBox(horizontalWidget);
checkBoxList.push_back(checkBox);
temp = QString("m_checkBox").arg(i);
checkBoxList[i]->setObjectName(temp);
checkBoxList[i]->setEnabled(true);
checkBoxList[i]->setGeometry(QRect(110, 20+i*40, 21, 22));
hLayout->addWidget(checkBox);
}
scrollArea->setWidget(scrollAreaWidgetContents);
//scrollArea->setWidgetResizable(true);
verticalLayout_5->addWidget(scrollArea);
The output window always looks like the following.
In this example the input by the user is 25 however you can see that the 21st button is cut off and 4 other buttons are not visible.
The size window problem occurring after scroll functionality started working.
You need to add your horizontalWidget to a vertical widget like so:
QVBoxLayout* vLayout = new QVBoxLayout();
for (int i = 0; i < numberOfSlices; i++)
{
QWidget *horizontalWidget = new QWidget();
vLayout->addWidget(horizontalWidget);
....
}
scrollAreaWidgetContents->setLayout(vLayout);
You second problem looks like it comes from this line:
scrollArea = new QScrollArea(verticalWidget);
You're adding scrollArea directly to verticalWidget, but to get it to lay out the way you want you need to put it in a layout. Try the following instead:
QVBoxLayout* l = new QVBoxLayout();
l->addWidget(sliceLabel); // or whatever you call it
l->addWidget(scrollArea);
l->addWidget(clearButton); // again, your name here
verticalWidget->setLayout(l);
Try playing around with the QScrollBarPolicy.
http://doc.qt.digia.com/qt/qabstractscrollarea.html#horizontalScrollBarPolicy-prop
I'm guessing that the default behavior isn't working because there is something strange going on with layouts.

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