Seite 1 von 1

Programmcrash bei QListView::setCurrentIndex

Verfasst: 10. September 2013 12:26
von sigvdr
Kurzbeschreibung:
Eine Klasse "Daten" wird gespeichert in einer Liste "QList<Daten *>". Daten aus dieser Liste werden in einem QListView (sortiert) angezeigt.
QListView erhält die Daten über ein "QSortFilterProxyModel" von einer aus "QAbstractListModel" abgeleiteten Klasse.

Mit "append(Daten *daten)" füge ich der Liste neue Daten hinzu und möchte anschließend in der ListView zur neuen Zeile springen. Vereinfacht mit diesem Ablauf(genaues siehe unten):

Code: Alles auswählen

int row = datenClass.count();  
datenList.insert(row,daten);
sort();
QModelIndex index = proxyModel->mapFromSource(datenClass->index(row,0));
ui->listView->setCurrentIndex(index);
Fehler:
Mit Qt4.8.1 läuft das Programm wie erwartet, aber bei Qt5.0 gibt es in der Zeile "setCurrentIndex(index)" Probleme und das Programm stürzt ab.

Hier ein Auszug aus dem Programmcode

Code: Alles auswählen

class Daten {
.....
.....
};

class DatenClass : public QAbstractListModel {
.....
.....
private:
	QList<Daten *> datenList;
};

int DatenClass::append(Daten *daten){
    int row = datenClass.count();
    emit layoutAboutToBeChanged();
    beginInsertRows(QModelIndex(),row,row+1);
    datenList.insert(row,daten);
    endInsertRows();
    emit layoutChanged();
    return row;
}

MainProg::MainProg(.....) {
	datenClass = new DatenClass(this);
	proxyModel = new QSortFilterProxyModel(this);
	proxyModel->setSourceModel(datenClass);
	ui->listView->setModel(proxyModel);
	.....
	connect(datenClass,SIGNAL(layoutChanged()),this,SLOT(sort()));

}
void MainProg::sort(void){
    proxyModel->sort(0);
}

int MainProg::setCurrentRowFromSource(int row){
    QModelIndex index = proxyModel->mapFromSource(datenClass->index(row,0));
    if ( index.isValid() ) {
        row = index.row();
        ui->listView->setCurrentIndex(index);
    }
    return row;
}


int row = datenClass->append(new Daten(.....));
setCurrentRowFromSource(row);

Re: Programmcrash bei QListView::setCurrentIndex ab Qt5

Verfasst: 10. September 2013 21:43
von sigvdr
Seit ich die beiden Zeilen mit "layoutAboutToBeChanged()" und "layoutChanged()" deaktiviert habe läuft (bisher) auch mit Qt5 alles wie geplant!

Code: Alles auswählen

int DatenClass::append(Daten *daten){
    int row = datenClass.count();
//    emit layoutAboutToBeChanged();
    beginInsertRows(QModelIndex(),row,row+1);
    datenList.insert(row,daten);
    endInsertRows();
//    emit layoutChanged();
    return row;
}
Auszug aus der Qt5 Doku:
When subclassing QAbstractItemModel or QAbstractProxyModel, ensure that you emit layoutAboutToBeChanged() before changing the order of items or altering the structure of the data you expose to views, and emit layoutChanged() after changing the layout.
Allerdings war ich bisher der Ansicht, jede Änderung der Daten oder Datenstruktur innerhalb des Models sollte von "layoutAboutToBeChanged" eingeleitet und von "layoutChanged" abgeschlossen werden. Aus welchem Grund konnte das jetzt (seit Qt5) stören?

Gruß Sig

Re: Programmcrash bei QListView::setCurrentIndex ab Qt5

Verfasst: 11. September 2013 19:39
von Christian81
beginInsertRows() ist meines Erachtens falsch:
http://qt-project.org/doc/qt-5.0/qtcore ... InsertRows

To append rows, insert them after the last row.

For example, as shown in the diagram, we append two rows to a collection of 4 existing rows (ending in row 3), so first is 4 and last is 5:
Du fügst ein Element ein, also beginInsertRows(QModelIndex(),row,row);

Vielleicht hilft das ja schon gegen den Crash.

Re: Programmcrash bei QListView::setCurrentIndex ab Qt5

Verfasst: 13. September 2013 17:04
von sigvdr
Hallo, danke für den Hinweis
Ich will ja wirklich nur ein Element einfügen.
Falsch war deshalb:

Code: Alles auswählen

beginInsertRows(QModelIndex(),row,row+1);
Richtig ist

Code: Alles auswählen

beginInsertRows(QModelIndex(),row,row);
und layoutChanged() war für den Crash nicht verantwortlich!

Gruß Sig

Re: Programmcrash bei QListView::setCurrentIndex

Verfasst: 14. September 2013 20:19
von sigvdr
Hallo,

noch ein Nachtrag:
und layoutChanged() war für den Crash nicht verantwortlich!
das hat doch nur vorübergehend so ausgesehen.

Code: Alles auswählen

MainProg::MainProg(.....) {
   datenClass = new DatenClass(this);
   proxyModel = new QSortFilterProxyModel(this);
   proxyModel->setSourceModel(datenClass);
   ui->listView->setModel(proxyModel);
   .....
   // connect(datenClass,SIGNAL(layoutChanged()),this,SLOT(sort()));
   connect(datenClass,SIGNAL(sortItAgain()),this,SLOT(sort()));
}

Code: Alles auswählen

int DatenClass::append(Daten *daten){
    int row = datenClass.count();
    emit layoutAboutToBeChanged();
    beginInsertRows(QModelIndex(),row,row);
    datenList.insert(row,daten);
    endInsertRows();
    // emit sortItAgain();  // Neue Zeile eingefügt
    emit layoutChanged();
    emit sortItAgain();   // Neue Zeile eingefügt
    return row;
}
Im ersten Programmentwurf hatte ich das SIGNAL "layoutChanged()" verknüpft mit einem SLOT "sort()" damit das neu eingefügte Element auch gleich richtig einsortiert wird (siehe QSortFilterProxyModel). Genau das hat dann nicht zuverlässig gearbeitet.
Jetzt ist der SLOT "sort()" mit dem neuen SIGNAL "sortItAgain()" verknüpft. Sende ich dieses SIGNAL vor "layoutChanged()" gibt es immer wieder Probleme mit Programmabstürzen. Wenn es allerdings erst nach "layoutChanged()" gesendet wird, ist (zumindest bisher) das Problem beseitigt.

Es scheint hier also auf die richtige Reihenfolge anzukommen.

Gruß Sig

Re: Programmcrash bei QListView::setCurrentIndex

Verfasst: 14. September 2013 20:58
von Christian81
Auch layoutChanged() mit einem Sort zu reagieren ist auch gena falsch herum da dies m.E. wiederum ein layoutChanged() aufruft:
This signal is emitted whenever the layout of items exposed by the model has changed; for example, when the model has been sorted. When this signal is received by a view, it should update the layout of items to reflect this change