Programmcrash bei QListView::setCurrentIndex

Alles rund um die Programmierung mit Qt
Antworten
sigvdr
Beiträge: 100
Registriert: 1. Juli 2008 19:29

Programmcrash bei QListView::setCurrentIndex

Beitrag 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);
Zuletzt geändert von sigvdr am 14. September 2013 19:57, insgesamt 2-mal geändert.
sigvdr
Beiträge: 100
Registriert: 1. Juli 2008 19:29

Re: Programmcrash bei QListView::setCurrentIndex ab Qt5

Beitrag 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
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Programmcrash bei QListView::setCurrentIndex ab Qt5

Beitrag 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.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
sigvdr
Beiträge: 100
Registriert: 1. Juli 2008 19:29

Re: Programmcrash bei QListView::setCurrentIndex ab Qt5

Beitrag 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
sigvdr
Beiträge: 100
Registriert: 1. Juli 2008 19:29

Re: Programmcrash bei QListView::setCurrentIndex

Beitrag 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
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Programmcrash bei QListView::setCurrentIndex

Beitrag 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
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Antworten