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);
}
Related
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.
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();
}
I have a table that is basically a QTreeWidget and I want to put a clickable widget, possibly a button inside it. Each row is a QTreeWidgetItem, but I don't see how I can add a button with QTreeWidgetItem::setData
Here is a modification to the example provided in Qt Documentation for QTreeWidget adding a QPushButton to the second item
ui->treeWidget->setColumnCount(1);
QList<QTreeWidgetItem *> items;
for (int i = 0; i < 10; ++i)
items.append(new QTreeWidgetItem((QTreeWidget*)0, QStringList(QString("item: %1").arg(i))));
ui->treeWidget->insertTopLevelItems(0, items);
ui->treeWidget->setItemWidget(items.value(1),0,new QPushButton("Click Me")); // Solution for your problem
For two push buttons side by side within an item,you can take this approach
QWidget *dualPushButtons = new QWidget();
QHBoxLayout *hLayout = new QHBoxLayout();
hLayout->addWidget(new QPushButton("Button1"));
hLayout->addWidget(new QPushButton("Button2"));
dualPushButtons->setLayout(hLayout);
ui->treeWidget->setItemWidget(items.value(1),0,dualPushButtons);
You can adapt this by adding properties to the buttons etc.
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.
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);