In QAbstractItemModel, some functions, such as beginInsertRows, beginRemoveRows, can be used to insert and remove rows. But how to implement replacing a row item by another one?
If I understand you right, you need to notify subcribed views about data changing (one row replaced by new, for views it means that data has been changed) for the specified model index:
// let's the row is index that we want to invalidate
QVector<int> roles;
roles << Qt::DisplayRole;
emit dataChanged(index(row, 0), index(row, columnCount()-1), roles);
If you want to change a lot of data, you should do something like this:
beginResetModel();
// change data
endResetModel();
If you change just one row, emitting dataChanged() should do the trick.
Related
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;
}
[..]
}
QComboBox keeps storing duplicate strings entered by the user, even if I call its member function QComboBox::setDuplicatesEnabled(false).
How can I store single copies of the strings even when the user inserts duplicates?
From Qt documentation:
duplicatesEnabled : bool
This property holds whether the user can enter duplicate items into the combobox.
Note that it is always possible to programmatically insert duplicate items into the combobox.
By default, this property is false (duplicates are not allowed).
Access functions:
bool duplicatesEnabled () const
void setDuplicatesEnabled ( bool enable )
As the documentation says:
This property holds whether the user can enter duplicate items into
the combobox. Note that it is always possible to programmatically
insert duplicate items into the combobox.
So this option doesn't affect string you set programmatically. You need to remove duplicates from your list manually.
For example, if you're storing your list in QStringList, duplicates can be easily removed using list = list.toSet().toList().
you need to check, if the userinput is valid (not duplicated or not) and catch the void editTextChanged ( const QString & text ) signal.
you could also derive your own class from QComboBox and overload the void keyPressEvent(QKeyEvent* event) // may be not the correct name
I have a working Qtableview with custom model subclassed QAbstractTableModel and QAbstractItemModel.
I have a Qlineedit, onclicked it will filter the view:
// model.cpp
setFilter(QString strFilter) function searches trough my intern QList (this Qlist is actually attached to model) and if match found then: m_filterSet.insert(i);
This all works great. Problem is, i have CRUD operations for the tableview (insert row, delete row..) which also work great! But when selecting a row from a filtered set, i need to somehow know where in my QList exactly is this selected row from the filtered set (QSet ).
ui.myView->selectionModel()->currentIndex().row();
obvious gives the wrong indexes counting for the current view.
How can i somehow extract the value (int) from the selected row in the QSet?
Because when i added this function to model:
foreach (const int &value, m_filterSet)
qDebug() << value;
It has printed out successfully all the i values, e.g: 3410, 3411, 3412 (those are my client id's)
If i could extract this ID for the selected row in Qset, i could write a function that iterates my intern QList, and find a matching, so to speak:
if(m_Intern[i].nClientID == nId){ // nId = value inside Qset for selected row in view
return nIdx;
}
Qt has a solution for your problem - just use QSortFilterProxyModel. You will need to:
Subclass it and write your own filtering function (filterAccpetsRow)
Proxy your original model through filtering one
Attach filtering model to a view
use QSortFilterProxyModel::mapToSource() to convert between indexes in filtered and original model.
This allows you to have more than one view with just one source data model, each view may have different filters.
I solved it after a while re thinking, i just needed to implement another function inside my model:
int myClass::screenIndex2DataIndex(int nIdxScreen)
{
if(m_bUseFilter)
{
int nIdx =-1;
for(int i=0;i<m_lstIntern.size();i++)
{
if(m_filterSet.contains(i))
{
nIdx++;
if(nIdx == nIdxScreen){
return i;
}
}
}
return -1; //not found
}
else{
return nIdxScreen;
}
}
This way i can find out for the present index on the filtered view, where it is in my intern list.
After this it's easy to get my nClientID trough a return: return m_lstIntern[idx].nClientId
I'm still struggling with using QListView, I'm trying to select one particular row in the view and I cannot figure out how to do this.
I found a similar question on StackOverflow which recommends using the createIndex() method of the model, however this method is protected (perhaps it used to be public but is not anymore) so that doesn't work for me. Any suggestion?
You can get the index of anything by just calling
QModelIndex indexOfTheCellIWant = model->index(row, column, parentIndex);
Then you can call setCurrentIndex(indexOfTheCellIWant) as bruno said in his answer.
If model contains just a standard list of items as opposed to a tree structure, then it's even easier. Because we can assume that the item is a root item - no parent.
QModelIndex indexOfTheCellIWant = model->index(row, column);
With a tree structure it is a little trickier, because we can't just specify a row and a column, we need to specify these with respect to a parent. If you need to know about this part let me know and I'll explain more.
Only one more thing to note. Selection is based on cells, not really rows. So if you want to ensure that when the user selects a cell (or you do through code) that the whole row is selected you can do that by setting the "selectionBehavior" on the itself.
list->setSelectionBehavior(QAbstractItemView::SelectRows);
You can use QAbstractItemView::setCurrentIndex ( const QModelIndex & index )
Fetch an instance of QModelIndex from the QListView's model and select it:
void selectRowInQListView(int row, QListView *listView) {
QModelIndex index = listView->model()->index(row, 0);
if (index.isValid()) {
//listView->selectionModel()->select(index, QItemSelectionModel::Select);
//listView->selectionModel()->select(index, QItemSelectionModel::Current);
listView->setCurrentIndex(index);
}
}
HI,
I have some data there in my tablewidget and it is editable. My intention is to take data from edited row and column. IS it possible. Is it possible to check the edited row and column.
Any specific signal is there?
Of course,
void QTableWidget::itemChanged ( QTableWidgetItem * item ) [signal]
This signal is emitted whenever the data of item has changed.
Add connect like this:
connect(tableWidget,
SIGNAL(itemChanged(QTableWidgetItem *)), this, SLOT(on_any_itemChanged(QTableWidgetItem *)));