QTableView - add named row - c++

Im using a using a QTableView in conjuction with table model derived from QAbstaractTableModel. Now I want to insert rows/columns (which represent states/input chars) but I need to pass a string as a label (which is then shown in the HeaderView) for state/input char down to my data classes.
bool TransTableModel::insertRows(int position, int rows, const QModelIndex &index)
{
beginInsertRows(index, position, position+rows-1);
garage->addUsedState("q6");
endInsertRows();
}
TransTableModel is my custom model. My problem: how do I obtain this string from user? In this case I hardcoded "q6" as a label, but I need this to be user-supplied. Is there a way? Should I open an input dialog? From the model class?

If the model needs that data, it should be provided by the view which could then open an input dialog.
You can add a member function to your model to pass that extra parameter and call it from the view class instead of calling the "regular" insertRows (you don't have to call insertRows in that new function either, just beginInsertRows and endInsertRows).

Related

qt - how to permanently sort a qstandarditemmodel

I have a program where I am trying to implement sorting on a qstandarditemmodel that is displayed in a table view. However, the method I am using doesn't seem to actually sort the model itself, but only the view. I need it to be able to sort the source model because I save the data to a .csv file using a delegate that passes the items from the model into an object of a class, and if the view is the only thing that is sorted it causes data loss due to the positions of the items in the view being changed but not in the model itself.
This is the code I use in the mainwidget constructor to connect the headerview clicked signal to a method that sorts the model:
currentStudentsModel = new QStandardItemModel(this);
ui->currentTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->currentTableView->setModel(currentStudentsModel);
ui->currentTableView->setItemDelegate(currentStudentsDelegate);
currentTableHeader = ui->currentTableView->horizontalHeader();
connect(currentTableHeader, SIGNAL(sectionClicked(int)), this, SLOT(on_sectionClicked(int)));
Here is on_sectionClicked():
void mainWidget::on_sectionClicked(int index)
{
currentStudentsModel->sort(index,Qt::AscendingOrder);
}
As I previously stated, this appears to only sort the items in the view as when I try to output all of the records stored in the model it has not changed from when they were initially entered. How do I get the model to be sorted itself and that order to be saved?
QStandardItemModel does not implements sort.
From Qt documentation:
void QAbstractItemModel::sort(int column, Qt::SortOrder order =
Qt::AscendingOrder)
Sorts the model by column in the given order. The base class
implementation does nothing.
You need to sort through QSortFilterProxyModel
currentStudentsProxyModel = new QSortFilterProxyModel;
currentStudentsModel->setSourceModel( currentStudentsProxyModel );
currentStudentsProxyModel->sort( 0, Qt::AscendingOrder );
void mainWidget::on_sectionClicked(int index)
{
currentStudentsProxyModel->sort(index,Qt::AscendingOrder);
}

Qt: Recognizing items of QTreeView

Example Tree View:
1. a
1.1. b
1.1.1. c
I want to know how I can make my code recognize I right clicked whether a or b or c. I am able to create a TreeView, add a b c to it, and get the item at the position of the right click but I don't know how I can recognize the item so right clicks would create different context menus respecting the item clicked. I use standard item model (QStandardItemModel) and so far what I got is:
void MyWindow::make_tree_custom_menu(const QPoint& pos){
QModelIndex index = treeView->indexAt(pos);
int itemRow = index.row();
int itemCol = index.column();
QStandardItem* itemAtPos = model->item(itemRow, itemCol);
itemAtPos->setText("meh");
}
I know that with QTreeWidgetItems you can do QTreeWidgetItem* newitem = new QTreeWidgetItem(name, itemtype); but as far as I could see in the docs, QStandardItem doesn't have such constructor. Also, I know that this exists, but it is unanswered. Therefore, I would like any help on possible methods to identify tree view items in such an application.
First of all, I suggest to use the QStandardItemModel::itemFromIndex(const QModelIndex & index) method to get the item in this case. The QStandardItemModel::item(int row, int column) method hasn't the parent parameter, so I think it returns only top level items (this method is good for lists or tables).
Then, when you get the item, you have exactly the pointer to the item you have created, so you have all you need to recognize it. If you want to set an attribute to the item to define a type (like QTreeWidgetItem itemType), you can use the QStandardItem::setData(const QVariant & value, int role) method (using e.g. the Qt::UserRole) when you create the item. Then you can get the item type using the QStandardItem::data(int role) method in your make_tree_custom_menu method.
See:
QStandardItemModel::itemFromIndex
QStandardItem::setData
QStandardItem::data
Qt::UserRole

Qt use same model for QListView and QTableView

I'm trying to use the same model to display pieces of information on 2 different kind of view.
I need to show information about 150 objects that have a description and can be either on or off.
One of the view is a summary of the on/off states of my objects in a table (15 by 10) of icons.
The other view is a list of 150 rows that display the state of the objects and their descriptions.
I tried to subclass a QAbstractTableModel and return a different rowCount and columnCount value depending of the view used and it works (my 150 objects are all shown in the table and in the list) but it remove the clear separation between model and view given that I need to specifically tell the model which view is using it and it doesn't work as expected when I try to link the 2 selection models of the views together because QModelIndex changes when the row and columns counts change.
Any idea of what could be a better approach to my problem or a way to solve the selection issue?
EDIT due to #ramtheconqueror response:
The response from ramtheconqueror led me to the right direction but surprisingly I discovered that the ProxyModel takes the overriden columnCount and rowCount function into account for the widget size calculation but do not actually use them for the widget indexes.
I mean that regarding my example above, the ListView using the new ProxyModel was a widget with enough space for 150 rows but was only showing the first 10 (rowCount in the source model).
Therefore, I did it the other way around and used a QAbstractListModel as the main model an set the QTableView to use the Proxy model but again the table was correctly a 15 by 10 widget but with content only on the first column.
I had to change the return value of ColumnCount to 15 on my QAbstractListModel subclass to get the correct result (even if it's really a list and therefore only contains one column).
Am I doing something wrong? I just don't understand why it behaves like that...
As you already have QAbstractTableModel, create a proxy model for the list view. Simple implementation would be like
TableModel* tableModel = new TableModel();
.....
tableView->setModel(tableModel);
class ListProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
virtual int columnCount(const QModelIndex& idx) const { return 1; }
virtual QVariant data(const QModelIndex& idx, int role) const {
... get the actual model index
... ask the tableModel for the actual data
... construct the data (string / int / bool etc)
return the data;
}
}
QListView listView = new QListView();
ListProxyModel* listModel = new ListProxyModel();
listModel->setModel(tableModel);
listView->setModel(listModel);

Qt: View not updating after QAbstractItemModel::beginInsertRows/endInsertRows

I have a QAbstractItemModel derived model attached to a QTreeView
I want to programatically append a single row to a node somewhere in my tree hierarchy.
I have a slot which is connected to a signal from my view. The signal sends the QModelIndex of the node to which I want to append my new row. In the slot I call beginInsertRows(...) using that QModelIndex and the new row's row number, append the new row to my model data, and then call endInsertRows():
The value passed to beginInsertRows(...) is the number of child rows the parent node has prior to appending the new node.
That is, if there are 4 child rows, they will have row indices 0, 1, 2 and 3. Therefore the new row number added will be 4.
void Model::slotOnAddRow(QModelIndex parent, std::string key)
{
assert(parent.isValid());
Row& parent_row = *static_cast<Row*>(parent.internalPointer());
beginInsertRows(parent, parent_row.numChildren(), parent_row.numChildren());
parent_row.addChildRow(key);
endInsertRows();
}
The problem I'm having is that after calling endInsertRows() my view does not update.
Example:
Here is an example of my tree view.
Scenario:
I want to append a new row to SPREAD_1.
SPREAD_1 currently has 4 children rows:
0: inst_id
1: LEG_1
2: LEG_2
3: LEG_3
The new row will therefore have row index 4, so I call beginInsertRows(SPREAD_1, 4, 4);
I do just this, and my view does not show my new row.
Proof the node does actually exist:
I know the row exists in my model, because if I collapse the SPREAD_1 node, and then re-expand it, my newly added row is now visible:
Question:
AFAIKT I've followed the example online correctly, but I'm obviously missing something.
How can I append a new row to a tree node, and have the view update?
Do I need to emit a signal or override another base class method?
An issue like this is indicative of an error elsewhere in the model. Without seeing the implementation of the model it is impossible to say where.
Using Model Test can be very helpful in diagnosing the issue.
Literally all you need to is instantiate a ModelTest instance with your model
QTreeView(&_model);
ModelTest test(&_model);
If the model doesn't conform, you will get assertion failures from ModelTest
I fixed this by adding ui->treeView->reset(); after insert rows
Make sure that the index passed to beginInsertRows is correct. In particular the column number for the index needs to be zero if the children are attached to column zero (which they normally are)!
Try to emit dataChanged signal with parent index as argument.

Delete right row from a table model that is sorted differently than the table view

I have a QStandardItemModel and a QTableView. The view is sorted differently than the model. I am using a QSortFilterProxyModel.
If I select a row in the view that I want to delete, how do I know to which row in the model that corresponds to?
From,
http://doc.qt.io/qt-5/qitemselectionmodel.html
QModelIndexList QItemSelectionModel::selectedIndexes() const
Returns a
list of all selected model item indexes. The list contains no
duplicates, and is not sorted.
This is accessible from the selection model of the view.
You also can determine which row in the view represents which QModelIndex. For Example QAbstractItemView::visualRect(const QModelIndex &) gets visual rect of an index. There is also indexAt(const QPoint &) to map in other direction.
And now since you added that you are using a proxy model, then use the proxy model's function to map to original model -- QSortFilterProxyModel::mapToSource
In summary,
get selection model and selected QModelIndex
map to original QModelIndex
delete in the original QStandardItemModel