I need to implement a table with Qt.
I believe I'll be suing a QAbstractTableModel, with a QTableView using this model.
I understand I'll have to edit the rowCount(), columnCount(), and data() functions of the model.
However, I don't understand how to exactly set the data inside the model, so that data() function can retrieve it..
Is the setData() function provided for this purpose? I have seen it takes EditRole as its parameter, which I don't want, as I don't want my table to be editable.
So, how do I "set" data inside the model, or have data for the model to get at, using data() function?
Also, how is the data() function called, i.e., who calls it and where would it need to be called?
Please help me with this.
Thanks.
How the actual data is kept in memory, generated or queried from a data store is completely up to you. If it's static data, you can use the Qt container classes or custom data structures.
You only need to reimplement the setData() method for editable models.
There are 4 methods you need to implement in a non-editable QAbstractTableModel subclass:
int rowCount()
int columnCount()
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole )
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole)
These methods are called from the view, usually a QTableView instance. The first two methods should return the dimensions of the table. For example, if rowCount() returns 10 and columnCount() returns 4, the view will invoke the data() method 40 times (once for each cell) asking for the actual data in your model's internal data structures.
As an example suppose you have implemented a custom slot retrieveDataFromMarsCuriosity() in your model. This slot populates a data structure and is connected to a QPushButton instance, so you get fresh data by clicking a button.
Now, you need to let the view know when the data is being changed so it can update properly. That's why you need to emit the beginRemoveRows(), endRemoveRows(), beginInsertRows(), endInsertRows() and its column counterparts.
The Qt Documentation has everything you need to know about this.
You don't need to use setData(...). Instead, you need to subclass QAbstractTableModel in such a way that its methods rowCount(), columnCount(), data(index) and potentially headerData(section, horizontalOrVertical) return the data you wish to display. Here's an example based on PyQt5:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
headers = ["Scientist name", "Birthdate", "Contribution"]
rows = [("Newton", "1643-01-04", "Classical mechanics"),
("Einstein", "1879-03-14", "Relativity"),
("Darwin", "1809-02-12", "Evolution")]
class TableModel(QAbstractTableModel):
def rowCount(self, parent):
# How many rows are there?
return len(rows)
def columnCount(self, parent):
# How many columns?
return len(headers)
def data(self, index, role):
if role != Qt.DisplayRole:
return QVariant()
# What's the value of the cell at the given index?
return rows[index.row()][index.column()]
def headerData(self, section, orientation, role):
if role != Qt.DisplayRole or orientation != Qt.Horizontal:
return QVariant()
# What's the header for the given column?
return headers[section]
app = QApplication([])
model = TableModel()
view = QTableView()
view.setModel(model)
view.show()
app.exec_()
It's taken from this GitHub repository and displays the following table:
Related
I'm a newbie with the Model/View programming of Qt and have read the Editable Tree Model Example in the Qt documentation. The nice feature in this example is that a single object (TreeItem) encapsulates two pieces of information that later are displayed in a single row containing two columns (name and description) thanks to overriding of QModelIndex QAbstractItemModel::index and QVariant QAbstractItemModel::data.
Now, I also have a custom class (e.g. Foo) containing two pieces of information (Foo::m_name and Foo::m_description) that I want to display in a single row containing two columns, but instead of subclassing QAbstractItemModel I want to subclass QStandardItemModel because it has some much functionality. However, it seems I must create two QStandardItem objects for each of my Foo objects, one to handle m_name and another to handle m_description. How can I keep a single Foo object in memory and have these two QStandardItem objects refer to it?
In my question there's the implicit assumption that one must create a QStandardItem object for each (row, column) pair. Please let me know if this is wrong.
QStandardItemModel is all about storing the data in the model, so each cell is represented by a QStandardItem which holds that cell's data.
If the data is already stored elsewhere and should not be duplicated, then the QStandardItemModel is the wrong approach and a custom model is the way to go.
A custom model, in the case of a tree structure derived from QAbstractItemModel, is just an interface between the view and the data, so the data resides only once in memory.
A post in qtcentre suggested Chapter 4 of Advanced Qt Programming and lo and behold, there's a discussion of a tree subsclassing QstandardItemModel and QStandardIteml where each row of the tree is made up of three QstandardItem handling different properties of a single object.
The implementation source code is freely available
Basically, one has:
class myItem : public QStandardItem {
public:
myItem(Foo &afoo) : QStandardItem(afoo.getName()), m_foo(afoo) {
m_description = new QStandardItem(afoo.getDescription());
}
QstandardItem *m_description; // display m_description
private:
Foo &m_foo;
};
and then we insert a row of two QstandardItem in our model tree
class myModel: public QStandardItemModel {
StandardItem *myModel::appendRow(QStandardItem *parent, Foo &afoo)
{
auto *doublet = new myItem(afoo);
parent->appendRow(QList<QStandardItem*>() << doublet
<< double->m_description);
return nameItem;
}
}
There is a QCompleter (set to QLineEdit) populated with QStandardItemModel. That model also populates the QTableView, I need to get the QModelIndex and select it in QTableView but it fails, it
passes text instead of QModelIndex:
completer.highlighted.connect(print_index)
passes only the first index:
completer.highlighted.connect(lambda : select_index(completer.currentIndex()))
def select_index(index):
table_view.setCurrentIndex(index)
I read docs, but cannot understand what do I do wrong.
http://doc.qt.io/qt-5/qcompleter.html#highlighted-1
There's two versions of the highlighted signal: the default one emits a string, the other emits a QModelIndex
To get the index, use:
completer.highlighted[QtCore.QModelIndex].connect(onHighlight)
But be careful, this is the index in the completion model, not the model you populated the completer with. You can use mapToSource to get the original index.
def onHighLight(index):
#completer model
print(index)
#model
sourceIndex=completer.completionModel().mapToSource(index)
print(sourceIndex)
I would like to use row() function of QmodelIndex. It will directly return the list index of your current selection.
I want to use a QTableView to show different roles of an item, e.g. column 1 shows the DisplayRole data, column 2 shows UserRole, column 3 shows UserRole+1, etc
I've created this by making my own item delegate and assigning it to the appropriate column. However, to get access to the same item the delegates have to access their siblings. For example, here's my setEditorData function:
void UserRoleDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QModelIndex baseIndex = index.sibling(index.row(),0);
QVariant v = baseIndex.data(Qt::UserRole);
if(v.isValid())
static_cast<QLineEdit*>(editor)->setText(v.toString());
}
Right now, it's hardcoded that column 0 contains the real object and other columns just access that via the sibling function. I'm worried, though, that it's fragile in that changing the column order will break it.
There are obvious ways to manage that, but I'm just wondering if I'm taking the right approach. Is there a better option to display different aspects of the same item?
To avoid possible code rewrite you simple could use the enumeration for your column numbers. For example:
enum ColumnNumber {
Base,
Sub1,
Sub2
}
And in your delegate's code:
[..]
QModelIndex baseIndex = index.sibling(index.row(), Base);
Thus, if you need to change the column order, you simply need to change the order of your enum values. The rest of code will work correctly as it is.
WRT to item delgate usage - it looks rather strange. Such things used to made in the model class, especially in QAbstractItemModel::data(...) function, using it with Qt::EditRole role. For example:
QVariant Model::data(const QModelIndex &index, int role) const
{
if (role == Qt::EditRole) {
// Get the data that stored in the first column
// ..
return data;
}
[..]
}
I know you can do it when you have access to the QStandardItemModel but using combobox->model() returns a QAbstractItemModel which doesn't have the item(int row, int col) accessor. I've tried working with QAbstractItemModel::itemData(QModelIndex) but can't get it to work as I require.
I just need to get the CheckState of the items, if(item.checkState() == Qt::Checked) etc...
Edit: I have this code, can I cast it to a QStandardItem?
QModelIndex index(1, 0);
QVariant item = ui->SearchAssessmentCombo->model()->data(index, Qt::CheckStateRole);
You can't declare an index yourself, all indices are tied to a model. Internally, the data() function will determine that the index you gave in the parameter does not belong to the model and will return null values for everything.
You need to ask your model to give you a valid index before you can use it.
QModelIndex index = ui->SearchAssessmentCombo->model()->index(1,0);
I'm developing an application using QT5.0 and new to QT. Badly, i have not too much time for a long learning curve.
I have derived my own TableModel and set it to a editable TableView. TableView shows model's data, it works. But when i activate a cell on the tableview, the data disappears. I looked at the documentation and saw that QTableView is derived from QAbstractItemView class which have a signal called 'activated' and a slot called 'edit'. So, i think 'activated' signal is connected to 'edit' slot. But 'edit' is not virtual, so i can not override it. I may connect my child class to parents 'activated' signal but actually i do not know how to handle this signal in order to save the current data of the TableView object.
There is no problem if the code uses SqlTableModel. I think it handles the 'activated' signal but I'm not sure about these, just speculating..
What is the right way to do this?
Check your the data function:
QVariant TableModel::data(const QModelIndex &index, int role) const
if( !index.isValid() )
return QVariant();
if( role == Qt::DisplayRole || role == Qt::EditRole) {
return <your data>
}
return QVariant();
}
Ensure that you process the EditRole role.
Good luck!