Hide future columns of QStandardItemModel in QTreeView - c++

I have a QStandardItemModel. This model might get additional columns via an input widget.
In addition, the QStandardItemModel is the model of a QTreeView.
I would like to guarantee that just the first n columns of the QStandardItemModel are visualised in the QTreeView.
How could I achieve this?
But:
The model is not aware of the view (expect Qt does something in the background)
The view is not informed about the updated columns by my code. Nevertheless, the new columns are visualised.
What is my motivation?
I would like to visualise the first n columns in a QTreeView. On selection of an item the remaining columns (of the row of the selected item) shall be presented in a QTableWidget as rows.

But:
The model is not aware of the view (expect Qt does something in the background)
The view is not informed about the updated columns by my code. Nevertheless, the new columns are visualised.
You are correct that the model is unaware of the view. This is exactly how it should be and is good practice. The Qt Model-View Framework is a good practical implementation of the Model-View-Controller (MVC) Pattern
Models are not supposed to know what's going to be viewed or not, thier responsibility is to store and organise data and properties of those data.
Views connect to models and have a read-only relationship with them. They must be notified when changes are made to the models so that they know that they must update themselves. In Qt this is done by connecting signals in the model to slots in the view. These connections are made in the function QAbstractItemView::setModel
Your question deals specifically with the display of columns and in Qt, the main Item View classes delegate the responsibility of column and row visibility to the QHeaderView class, which is created automatically by all views by default.
If you want to create special functionality, you need to either manipulate these default views, or else setting a custom header view to the main view.
I've done the second option.
I've also connected the model to two views, one that is limited to show only the first 5 columns and the second one without a custom header view. This is to show that the underlying model is completely unaware of the viewing restrictions and still contains the complete data set.
#include <QtWidgets/QApplication>
#include <QtWidgets/qtreeview.h>
#include <QtGui/qstandarditemmodel.h>
#include <QtWidgets/QHeaderView>
class RestrictedHeaderView : public QHeaderView {
Q_OBJECT
public:
RestrictedHeaderView(int cols, QWidget *parent = 0) : QHeaderView(Qt::Horizontal, parent), visibleColumns(cols) {}
protected slots:
virtual void sectionsInserted(QModelIndex const &parent, int logicalFirst, int logicalLast){
if (!parent.isValid() && logicalLast >= visibleColumns){
for (int col = visibleColumns; col <= logicalLast; ++col){
hideSection(col);
}
}
}
private:
int visibleColumns;
};
#include "main.moc"
int main(int argc, char** argv){
QApplication app(argc, argv);
QTreeView view;
view.setWindowTitle("Limited View");
QTreeView view2;
view2.setWindowTitle("Complete View");
QStandardItemModel model(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);
}
}
// Apply the model to both views and show them
view.setModel(&model);
view.show();
view2.setModel(&model);
view2.show();
// set a custom header to the limited view only so that it automatically hides all columns that are inserted after the fifth column
view.setHeader(new RestrictedHeaderView(5));
// Add new columns to the underlying model
model.insertColumns(4, 3);
for (int row = 0; row < 4; ++row){
for(int column = 4; column < 7; ++column){
QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
model.setItem(row, column, item);
}
}
return app.exec();
}

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 change the background color from the header(horizontal / vertical) QTableWidget on Qt?

I would like to know how to change the background color from the headers (horizontal / vertical) from the object QTableWidget on Qt.
I already Know how to change all the headers together, using :
ui->tableWidget->setStyleSheet("QHeaderView::section {background-color:red}");
But I need change individually the items. Obviously if this is possible .
There are at least 2 ways to solve this problem. Very easy one:
Just use setHeaderData() and set specific colors for specific sections.
QTableView *tview = new QTableView;
QStandardItemModel *md = 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));
md->setItem(row, column, item);
}
}
tview->setModel(md);
tview->model()->setHeaderData(0,Qt::Horizontal,QBrush(QColor("red")),Qt::BackgroundRole);
tview->show();
But u nfortunately it will not work on some systems... Qt uses the platform style. For example, my Windows doesn't allow to change header's color. So this code doesn't work on my machine. Fortunately, it can be solved easily with changing global style. So next code works:
//... same code ...
tview->show();
QApplication::setStyle(QStyleFactory::create("Fusion"));
If you don't want to change style, then you should create your own HeaderView. Probably, something similar as here.

How to add QPushButton in QTableView when loading database by C++

I'm working on a chat application. In the chat view, where I show the chat messages using a QTableView, I want to add a QPushButton next to each message. Example:
A: How are you ? --- Button
B: I am fine --- Button
But I want to add only 10 rows. When scrolling, the data will change in the 10 rows, but I don't want to create new rows. And I want to know how to put the QPushButtons into the QTableView. How can I do this?
And I want to know how to put the QPushButtons into the QTableView
You can achieve this using setIndexWidget ( const QModelIndex & index, QWidget * widget ) member function. Something like:
QTableView * table = new QTableView (this);
...
int column = 1;
for (int row = 0; row < 10; ++row) {
QPushButton * button = new QPushButton (tr("Button Name"), table);
table->setIndexWidget (model->index (row, column, QModelIndex () ), button);
}

Setting text below the icon rather than on right in QTableView?

I'm adding images as items to a QTableView, I'm also adding a specific text to each images, problem is the text is shown beside the image or the icon, but I want QTableView to show it below the image or the icon. My code snippet is as below:
QStandardItemModel * model = new QStandardItemModel(NumOfRow, 3, this);
Then comes this part which is in the loop
//
QStandardItem * itm = new QStandardItem;
itm->setIcon(image);
itm->setText(text);
model->setItem(row, column, itm);
//
Then this part outside the loop
ui->listOfImages->setModel(model);
ui->listOfImages->setStyleSheet(QString("icon-size: 150px 150px"));
ui->listOfImages->verticalHeader()->setVisible(false);
ui->listOfImages->horizontalHeader()->setVisible(false);
for(int i=0; i<=rowPointer; i++)
{
ui->listOfImages->setRowHeight(i,150);
}
for(int j=0; j<3; j++)
{
ui->listOfImages->setColumnWidth(j,150);
}
Could you say me if there is any way to put the name below the icon rather than in the right side of the icon?
Thanks
Well, I would try to handle the text alignment with the custom QStandardItemModel sub class. Here is the sample model, that implements it:
class ItemModel : public QStandardItemModel
{
public:
ItemModel(int rows, int columns, QObject *parent = 0)
:
QStandardItemModel(rows, columns, parent)
{}
QVariant data(const QModelIndex &index, int role) const
{
if (role == Qt::TextAlignmentRole) {
return Qt::AlignBottom; // <- Make alignment look different, i.e.
// <- text at the bottom.
} else {
return QStandardItemModel::data(index, role);
}
}
};
So, instead of using the Qt class, you will need to create an instance of this custom class:
QStandardItemModel * model = new ItemModel(NumOfRow, 3, this);
The rest will remain the same.

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