dynamically add rows to tableview - c++

I want to have a table view in my Qt code. It has four column and many rows (not know ) before hand in table view Qt how can I dynamically add rows as
QStandardItemModel model(0,2);
What to do add rows dynamically?

there is a huge set of functions for that ,
void appendColumn ( const QList<QStandardItem *> & items )
void appendRow ( const QList<QStandardItem *> & items )
void appendRow ( QStandardItem * item )
void insertColumn ( int column, const QList<QStandardItem *> & items )
bool insertColumn ( int column, const QModelIndex & parent = QModelIndex() )
void insertRow ( int row, const QList<QStandardItem *> & items )
bool insertRow ( int row, const QModelIndex & parent = QModelIndex() )
void insertRow ( int row, QStandardItem * item )
look in qt docs for their description
UPD:
QStandardItemModel m(3,3);
QList<QStandardItem*> newRow;
for (int i=0;i<m.colCount();i++)
{
QStandardItem* itm = new QStandardItem(QString("data for col %1").arg(i));
newRow.append(itm);
}
m.append(newRow);
haven't test it but it should work

Related

Qcombobox remove and add item in C++ Qt

Creating a combobox in Qtableview2 column 1 and passing values from Qtableview1 column1
so i am storing column1 table1 values in Qstringlist and passing to combobox
void cymodel::rowvalues() {
QAbstractItemModel* table1 = ui.tableView->model();
QAbstractItemModel* table2 = ui.tableView_2->model();
QStringList colvallist1;
for (int r = 0, maxI = table1->rowCount(); r < maxI; ++r)
colvallist1.append(table1->data(table1->index(r, 0)).toString());//store value in stringlist
for (int i = 0, maxI = table2->rowCount(); i < maxI; ++i)//for all rows
{
const QModelIndex idx = table2->index(i, 1);
QComboBox* combo = qobject_cast<QComboBox*>(ui.tableView_2->indexWidget(idx));
if (!combo)
{
combo = new QComboBox(); // make combo
ui.tableView_2->setIndexWidget(idx, combo);// add combo
}
// combo->model()->removeRows(0, combo->count(), combo->rootModelIndex());
colvallist1.removeDuplicates(); // clear duplicates in colvallist1
colvallist1.removeAll(QString("")); //remove empty row data
combo->setPlaceholderText(QString(" "));
combo->addItems(colvallist1);
}
}
connect(ui.tableView->model(), &QAbstractItemModel::dataChanged, this,
&cymodel::rowvalues);
Using removerows() all items are removed everytime,,if I added some values in column0 table1 then select in combobox and again adding values in table1 Column0 that time combobox selection go away
but if i don't use removerows() then when I'm adding new item to combobox then its adding multiple time like 2 values added then i add 2 more in table1 col then in combobox it become 4
So, how to add those that appear in colvallist1 but are not already in the combo & remove those that don't appear in colvallist1 ??
Thanks Plz help!!
thnks,,,i solved this..
to remove the items in combobox which are not in colvallist1..without change in selection
here is the code--
void cymodel::rowvalues() {
QAbstractItemModel* table1 = ui.tableView->model();
QAbstractItemModel* table2 = ui.tableView_2->model();
QStringList colvallist1;
for (int r = 0, maxI = table1->rowCount(); r < maxI; ++r)
colvallist1.append(table1->data(table1->index(r, 0)).toString());//store value in stringlist
for (int i = 0, maxI = table2->rowCount(); i < maxI; ++i)//for all rows
{
const QModelIndex idx = table2->index(i, 1);
QComboBox* combo1 = qobject_cast<QComboBox*>(ui.tableView_2-
>indexWidget(idx));
if (!combo1)
{
combo1 = new QComboBox(); // make combo
ui.tableView_2->setIndexWidget(idx, combo);// add combo
}
colvallist1.removeDuplicates(); // clear duplicates in colvallist1
colvallist1.removeAll(QString("")); //remove empty row data
combo1->setPlaceholderText(QString(" "))
QString selected = combo1->currentText();
int indx = combo1->currentIndex();
combo1->clear();
combo1->addItems(colvallist1);
combo1->findData(selected);
combo1->setCurrentIndex(indx);
combo1->setCurrentText(selected);
}

removeRows() and QPersistentModelIndex

I have implemented my own QAbstractListModel which is based on an std::vector. I now want to display the contents of this model in a QGraphicsScene. For this I have implemented my own QGraphicsItem which stores a QPersistentModelIndex as a pointer to the data.
I have implemented the removeRows method as follows:
bool VectorModel::removeRows(int row, int count, const QModelIndex& parent) {
if (row + count < _vector.size()) {
beginRemoveRows(QModelIndex(), row, row + count);
_vector.erase(_vector.begin() + row, _vector.begin() + row + count);
endRemoveRows();
return true;
}
return false;
}
Now since I erase some elements, the index of the following elements will change. Because of this the QPersistentModelIndex needs to be adjusted.
I have found the changePersistentIndex() method in QAbstractItemModel and I know that I can get all persistent indices with persistentIndexList(). However I don't know how to adjust the indices accordingly using this method. How can this be done?
Will changing these indices be enough to prevent Invalid index errors?
Update
I have changed the removeRows() with the enhancements of #Sebastian Lange, however it is still not working as expected and I receive Invalid index errors:
bool LabelModel::removeRows(int row, int count, const QModelIndex& parent) {
Q_UNUSED(parent)
if (row + count < _vector.size()) {
beginRemoveRows(QModelIndex(), row, row + count);
_vector.erase(_vector.begin() + row, _vector.begin() + row + count);
endRemoveRows();
auto pil = persistentIndexList();
for(int i = 0; i < pil.size(); ++i)
{
if (i >= row + count) {
changePersistentIndex(pil[i], pil[i-count]);
}
}
return true;
}
return false;
}
The emitted errors look like this (when removing the 7th element):
QAbstractItemModel::endRemoveRows: Invalid index ( 7 , 1 ) in model QAbstractListModel(0x101559320)
QAbstractItemModel::endRemoveRows: Invalid index ( 8 , 1 ) in model QAbstractListModel(0x101559320)
QAbstractItemModel::endRemoveRows: Invalid index ( 9 , 1 ) in model QAbstractListModel(0x101559320)
QAbstractItemModel::endRemoveRows: Invalid index ( 10 , 1 ) in model QAbstractListModel(0x101559320)
QAbstractItemModel::endRemoveRows: Invalid index ( 6 , 1 ) in model QAbstractListModel(0x101559320)
Well, you do not need to fiddle with changePersistentIndex, calling beginRemoveRows and endRemoveRows will automatically update all the persistent indexes currently existing on the model. The only invalid QPersistentModelIndex you should have after removing rows are the index on rows which have been actually deleted

Binding model to Qt TableView

Im very new in Qt programming i want to display data with Qt TableView which im getting from XML file.
I Cannot find any useful tutorial about how to create custom model and then bind it to TableView.
Please provide some help or if anybody have some good examples please share.
Thanks
The model-view approach in Qt is quite versatile. All models inherit from QAbstractItemModel. With this class you can create quite complex data layouts (lists, trees, tables etc.) but the effort for the implementation is in comparison quite high, too.
A ready to use class would be QStandardItemModel. You can easily create a table model and add the items, which are instances of QStandardItem. You can use the following code to get started:
#include <QtGui>
QStandardItemModel* createModel(QObject* parent)
{
const int numRows = 10;
const int numColumns = 10;
QStandardItemModel* model = new QStandardItemModel(numRows, numColumns);
for (int row = 0; row < numRows; ++row)
{
for (int column = 0; column < numColumns; ++column)
{
QString text = QString('A' + row) + QString::number(column + 1);
QStandardItem* item = new QStandardItem(text);
model->setItem(row, column, item);
}
}
return model;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMainWindow window;
QTableView* view = new QTableView;
view->setModel(createModel(view));
window.setCentralWidget(view);
window.show();
return app.exec();
}
You see, it is really easy to use. However, a drawback is that you have to supply the data via a QStandardItem, which might be a waste of memory. For example, assume you have several 100MB of data, which you would like to display in a view. As you already have the data stored somewhere, it would be preferable to just adapt it such that it can be used in the view instead of creating a QStandardItem for every cell.
This is where QAbstractTableModel comes into play. The following example creates a matrix
with 250.000 entries. Instead of creating one QStandardItem for every matrix element,
we sub-class QAbstractTableModel and implement the three pure virtual methods
numRows(), numColumns() and data(), which return the number of rows, columns and
the data to display.
#include <QtGui>
class MatrixModel : public QAbstractTableModel
{
public:
MatrixModel(int numRows, int numColumns, double* data)
: m_numRows(numRows),
m_numColumns(numColumns),
m_data(data)
{
}
int rowCount(const QModelIndex& parent = QModelIndex()) const
{
return m_numRows;
}
int columnCount(const QModelIndex& parent = QModelIndex()) const
{
return m_numColumns;
}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
{
if (!index.isValid() || role != Qt::DisplayRole)
return QVariant();
// Return the data to which index points.
return m_data[index.row() * m_numColumns + index.column()];
}
private:
int m_numRows;
int m_numColumns;
double* m_data;
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// Create a matrix.
const int numRows = 500;
const int numColumns = 500;
double matrix[numRows][numColumns];
for (int i = 0; i < numRows; ++i)
for (int j = 0; j < numColumns; ++j)
matrix[i][j] = i + j;
// Create a model which adapts the data (the matrix) to the view.
MatrixModel model(numRows, numColumns, (double*)matrix);
QMainWindow window;
QTableView* view = new QTableView;
view->setModel(&model);
window.setCentralWidget(view);
window.show();
return app.exec();
}
As you can see, the model does not duplicate any data but just serves as an adapter. If you need even more flexibility, you can go to QAbstractItemModel and event implement
the creation of the model indexes, which Qt uses to specify which model data to read or
write.
Do you want a custom model because you want it to be able to actively read and update from a constantly changing XML source?
If not, you can simply use any normal QTableView tutorial and a QStandardItemModel. Parse the XML file yourself and create QStandardItem objects, adding them into your model.
A custom model is a way to integrate the reading of the XML file directly into the model itself, as opposed to an Item-based approach where you populate it externally.
This should probably get your started, http://doc.qt.io/qt-5/modelview.html

How to select next row in QTableView programmatically

I have QTableView subclass that I am marking and saving its state with this :
connect(this,
SIGNAL(clicked(const QModelIndex &)),
this,
SLOT(clickedRowHandler(const QModelIndex &))
);
void PlayListPlayerView::clickedRowHandler(const QModelIndex & index)
{
int iSelectedRow = index.row();
QString link = index.model()->index(index.row(),0, index.parent()).data(Qt::UserRole).toString();
emit UpdateApp(1,link );
}
now i like programmatically to move the selection to the next row (not by pressing the row with the mouse)
and invoking clickedRowHandler(...) how shall i do that ?
Thanks
You already have the current row index, so use something like the following to get the modelindex for the next row
QModelIndex next_index = table->model()->index(row + 1, 0);
Then you can set that modelindex as the current one using
table->setCurrentIndex(next_index);
Obviously you'll need to make sure you're not running past the end of the table, and there's probably some extra steps to make sure the entire row is selected, but that should get you closer.
/*
* selectNextRow() requires a row based selection model.
* selectionMode = SingleSelection
* selectionBehavior = SelectRows
*/
void MainWindow::selectNextRow( QTableView *view )
{
QItemSelectionModel *selectionModel = view->selectionModel();
int row = -1;
if ( selectionModel->hasSelection() )
row = selectionModel->selection().first().indexes().first().row();
int rowcount = view->model()->rowCount();
row = (row + 1 ) % rowcount;
QModelIndex newIndex = view->model()->index(row, 0);
selectionModel->select( newIndex, QItemSelectionModel::ClearAndSelect );
}

Printing QTableView using render method

I am trying to print a table view. To fill a table view I have created my own model. To print table I am doing following:
QPrinter printer;
QPrintDialog printDialog( &printer, 0);
if( QDialog::Accepted == printDialog.exec() ) {
if( QPrinter::Landscape != printer.orientation() ) {
printer.setOrientation(QPrinter::Landscape);
}
QPoint startPoint = QPoint(20, 20);
QRegion printRegion = QRegion( 20, 20, printer.paperRect().width(),printer.paperRect().height() );
for( int i = 0; i < m_tables.size(); ++i ) {
tableView->render( &printer, startPoint, printRegion, QWidget::DrawChildren );
}
}
The issue is that I am printing into PDF file and there I am able to see only a small part of the table. I thought that changing the region parameter could help, but in the fact not. Any suggestions how to fix this?
Ok, here is my solution. Would be nice to hear your opinion.
PrintTableModel* pTableModel = new PrintTableModel();
QTableView* pTableView = new QTableView;
pTableView->setModel(pTableModel);
int width = 0;
int height = 0;
int columns = pTableModel->columnCount();
int rows = pTableModel->rowCount();
pTableView->resizeColumnsToContents();
for( int i = 0; i < columns; ++i ) {
width += pTableView->columnWidth(i);
}
for( int i = 0; i < rows; ++i ) {
height += pTableView->rowHeight(i);
}
pTableView->setFixedSize(width, height);
pTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
pTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
This code helped me. To print the table correctly, you just can perform a render call:
pTableView->render(printer);
You might try void QPrinter::setResolution ( int dpi ) to force a number of widget pixels per printer inches, effectively zooming your widget on the printout.