[gelöst] Problem mit QAbstract Item Model

Alles rund um die Programmierung mit Qt
AuE
Beiträge: 918
Registriert: 5. August 2008 10:58

[gelöst] Problem mit QAbstract Item Model

Beitrag von AuE »

Hi, habe da ein recht seltsames Problem mit dem QAbstract Item Model bzw mein Ansatz ist falsch.

Ich habe mir eine Klasse gebaut, welche vom QSqlQueryModel abgeleitet ist

Code: Alles auswählen

class MyCustomSqlModel :public QSqlQueryModel
in Ihr überschreibe ich nur die Fkt

Code: Alles auswählen

QVariant data(const QModelIndex &item,int role = Qt::DisplayRole ) const;	
So jetzt möchte ich dann die Daten des Models an ne QTableView übergeben. Das ganze mit setModel.

Soweit klappt das auch alles ganz gut.

Nun zum Problem. Das ganze klappt nur ca 1-2 mal. Danach fliege ich jedesmal mit einer Exception raus.

Hier der Code den ich (zyklisch bzw beim drücken eines Buttons) aufrufe

Code: Alles auswählen

void MyClass::on_AuftraglisteRefreshTimer()
{
	
	QSqlQuery query(objQSqlDb);
	QString query_for = tr(	"Select    (Select count(*) from T_auftrag) as Gesamt"
							", (Select count(*) from T_auftrag where Status = 0) as Warten"
							", (Select count(*) from T_auftrag where Status = 1) as Online"
							", (Select count(*) from T_auftrag where Status = 2) as Storno");
	
	query.exec(query_for);
	while (query.next())
	{//		query.record().value("Gesamt").toInt();
		ui.labelGesamtDSSHOW->setText(QString("%0").arg(query.record().value("Gesamt").toString()));
		ui.labelinBEARBEITUNGSHOW->setText(QString("%0").arg(query.record().value("Online").toString()));
		ui.labelSTORNOSHOW->setText(QString("0").arg(query.record().value("Storno").toString()));
		ui.labelWARTENDSHOW->setText(QString("%0").arg(query.record().value("Warten").toString()));
	}
	
	query.clear();

	//modelSQL.clear();
	
	modelSQL.setQuery("Select Zustand = case STATUS when 0 then 'wartet' when 1 then 'in Bearbeitung' when 2 then 'storniert' else 'keine information'	end , DATUM, FAHRZEUGKENNNUMMER ,TAKT, PR_NR,  MODELL from T_Auftrag order by Status DESC, Datum", objQSqlDb);
	
	if (modelSQL.lastError().text().length() > 3)
		WriteMessage( QString("ERROR %0").arg(modelSQL.lastError().text()) );
	ui.tableView->setModel(&modelSQL);
	
	ui.tableView->show();


	ui.tableView->resizeColumnsToContents();
	ui.tableView->resizeRowsToContents();

	// ausdehnung komplett nutzen
	ui.tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
	ui.tableView->verticalHeader()->setResizeMode(QHeaderView::Stretch);
	ui.tableView->horizontalHeader()->resizeSections(QHeaderView::Stretch);
	ui.tableView->verticalHeader()->resizeSections(QHeaderView::Stretch);
	
}

Die Funktion schmiert mir nach ui.tableView->setModel(); ab. Eine Exception in qabstractitemmodel.cpp in der Fkt

Code: Alles auswählen

void QAbstractItemModelPrivate::rowsAboutToBeRemoved(const QModelIndex &parent,
                                                     int first, int last)
dierekt am Ende.

Komisch sind hier die Übergabepara´s... der parent r=???c =??? d=???
und auch die Indexe lassen auf Sinnloses schließen (tierischHohe Werte)

Einer von euch ne Idee was ich falschmache oder eine Idee wie ich das besser machen könnte? Ziel ist es das ich zyklisch eine Tabelle aktualisiere bzw auch durch Userinteraktion mal zwischendurch.
Gibt es schon ne Update Fkt und ich muss net jedesmal das model neu von Hand setzen? Oder wie mache ich das am Geschicktesten?

Besten dank schonmal im Vorraus
Zuletzt geändert von AuE am 17. November 2008 14:36, insgesamt 1-mal geändert.
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag von upsala »

Das interessantere daran wäre der Stack-Trace, da der Fehler in rowsAboutToBeRemoved nur noch ein Folgefehler zu sein scheint.
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Re: Problem mit QAbstract Item Model

Beitrag von pfid »

AuE hat geschrieben: Einer von euch ne Idee was ich falschmache oder eine Idee wie ich das besser machen könnte? Ziel ist es das ich zyklisch eine Tabelle aktualisiere bzw auch durch Userinteraktion mal zwischendurch.
Gibt es schon ne Update Fkt und ich muss net jedesmal das model neu von Hand setzen? Oder wie mache ich das am Geschicktesten?

Besten dank schonmal im Vorraus
Musst du überhaupt jedesmal ein setModel() machen, oder tuts auch ein select() ?

Die Funktion sieht aus als ob sie das Intialisieren der Objekte und das Refreshen gleichzeitig machen soll, und das eben wiederholt.
AuE
Beiträge: 918
Registriert: 5. August 2008 10:58

Beitrag von AuE »

Hi,

anbei ein Ausschnitt aus der Aufrufliste. Hast du irgendeine Idee?

Evtl kann ich das ja auch umgehen. Ich suche halt nach einer Möglichkeit mich quasi live mit meiner Query an ne DB zu hängen.
Dateianhänge
fehler_QODBC.JPG
fehler_QODBC.JPG (184.57 KiB) 7988 mal betrachtet
AuE
Beiträge: 918
Registriert: 5. August 2008 10:58

Beitrag von AuE »

@ pfid.

Ja das tut Sie und nein, das muss sie prinzipiell nicht.

Beim ersten Klick auf den Button: Model setzen mit entspr. Query, Timer starten.

Bei jedem weiteren Zugriff bzw Timer Event soll sie nur noch aktualisiert werden. Bau das mal eben um evztl is das ja schon scheiße ;-)

Welches Select meinst du? Habe hier grad nur ein SelectAll gesehen?! Meinst du das oder wo kann ich doku finden?
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

Sorry mein Fehler, du benutzt ein Query Model, das select() gibts bei den Table-Models. Zum refresehen sollte das setQuery() reichen.
AuE
Beiträge: 918
Registriert: 5. August 2008 10:58

Beitrag von AuE »

So hab das ganze jetzt mal ein wenig umgebaut. Also mein StackedWidget reagiert wie folgt wenn die Site mit der QTableView aufgerufen wird:

Code: Alles auswählen

	else if (icurrentIndex == ui.stackedWidget->indexOf(ui.pageAuftragliste))
	{
		if(!objQSqlDb.isOpen())
		{
			objQSqlDb=QSqlDatabase::addDatabase("QODBC", "SQL_SERVER"); 
			objQSqlDb.setDatabaseName( QString(the_configuration::instance()->get_string("Datenbank","Auftragliste_Connect").c_str()) ); 
			
			if(!objQSqlDb.open())
			{
				
				WriteMessage(QString("Error. Sql Database couldn´t be opend...%0").arg(objQSqlDb.lastError().text()));
				return;
			}
		}		
		on_AuftraglisteRefreshTimer();
		AuftraglisteRefreshTimer->start (10000); // set timer, refreshing the table every 10s
		
	}


Dabei wird die Fkt aufgerufen:

Code: Alles auswählen

on_AuftraglisteRefreshTimer()
{
	
	QSqlQuery query(objQSqlDb);
	QString query_for = tr(	"Select    (Select count(*) from T_auftrag) as Gesamt"
							", (Select count(*) from T_auftrag where Status = 0) as Warten"
							", (Select count(*) from T_auftrag where Status = 1) as Online"
							", (Select count(*) from T_auftrag where Status = 2) as Storno");
	
	query.exec(query_for);
	while (query.next())
	{//		query.record().value("Gesamt").toInt();
		ui.labelGesamtDSSHOW->setText(QString("%0").arg(query.record().value("Gesamt").toString()));
		ui.labelinBEARBEITUNGSHOW->setText(QString("%0").arg(query.record().value("Online").toString()));
		ui.labelSTORNOSHOW->setText(QString("%0").arg(query.record().value("Storno").toString()));
		ui.labelWARTENDSHOW->setText(QString("%0").arg(query.record().value("Warten").toString()));
	}
	
	query.clear();	
	
	if (modelSQL.lastError().text().length() > 3)
		WriteMessage( QString("ERROR %0").arg(modelSQL.lastError().text()) );

	modelSQL.setQuery("Select Zustand = case STATUS when 0 then 'wartet' when 1 then 'in Bearbeitung' when 2 then 'storniert' else 'keine information'	end , DATUM, FAHRZEUGKENNNUMMER ,TAKT, PR_NR,  MODELL from T_Auftrag order by Status DESC, Datum", objQSqlDb);
	if (ui.tableView->model() == NULL)
	{	ui.tableView->setModel(&modelSQL);
	    ui.tableView->reset();
		ui.tableView->show();
	}
}

So und hier is das Ergebnis.... schaut euch vor allem mal die Werte an die AboutToBeRemoved bekommt...
Dateianhänge
fehölerODBC_II.JPG
fehölerODBC_II.JPG (146.99 KiB) 7972 mal betrachtet
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

Das db-open brauchst du eigentlich nur einmal pro Applikation machen, die QDatabase Instanz ist statisch. Der Sql-Query brauchst du die DB auch nicht mitgeben, da automatisch die default Instanz verwendet wird. D.h. du müsstest dir die DB nicht speichern.

Pack das addDatabase & open doch einmalig in deine Init-Funktion, und mach ansonsten nur noch normale QSqlQuery & setQuery aufrufe.
AuE
Beiträge: 918
Registriert: 5. August 2008 10:58

Beitrag von AuE »

Scheint was dran zu sein... hab grade folgendes in der Ausgabe gefunde
QSqlDatabasePrivate::removeDatabase: connection 'SQL_SERVER' is still in use, all queries will cease to work.
QSqlDatabasePrivate::addDatabase: duplicate connection name 'SQL_SERVER', old connection removed.

was mich nur verwundert ist das mir objQSqlDb.isOpen() jedesmal false zurckliefert...



Holy shit....

Alkso hab das mal mit innen konstruktor gepackt wie du sagtest...das is wirklich hier ein Prob. Wenn ich jetzt in die Fkt rein komme steht in der Ausgabe nach dem exec befehl "database not open"

da gibts doch sicher wo ein Flag das sie dauerhaft offen bleiben soll.....
AuE
Beiträge: 918
Registriert: 5. August 2008 10:58

Beitrag von AuE »

Das ist ja sehr komisch.....

Code: Alles auswählen

objQSqlDb=QSqlDatabase::addDatabase("QODBC", "SQL_SERVER"); 

objQSqlDb.setConnectOptions("SQL_ATTR_ACCESS_MODE=SQL_MODE_READ_ONLY;SQL_ATTR_TRACE=SQL_OPT_TRACE_ON"); // set ODBC 

objQSqlDb.setDatabaseName( QString(the_configuration::instance()->get_string("Datenbank","Auftragliste_Connect").c_str()) ); 

objQSqlDb.setUserName(QString(the_configuration::instance()->get_string("Datenbank","User", "sa").c_str())); 

objQSqlDb.setPassword(QString(the_configuration::instance()->get_string("Datenbank","User","sqlaueadmin").c_str())); 

objQSqlDb.setHostName(QString(the_configuration::instance()->get_string("Datenbank","Server","sqles01").c_str())); 

if(!objQSqlDb.open())

{

WriteMessage(QString("Error. Sql Database couldnït be opend...%0").arg(objQSqlDb.lastError().text()));

return;

}

else

{

modelSQL.setQuery("select * from T_Auftrag", objQSqlDb); /// is called!!!!!

}


und ein paar lines tiefer sagt er mir das es net mehr offen ist????
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

QSqlDatabase::database().isOpen() gibt false zurück?
AuE
Beiträge: 918
Registriert: 5. August 2008 10:58

Beitrag von AuE »

if(!objQSqlDb.isOpen())
{
}


da läuft er mir dauerd rein..... obwohl er ja oben aufgemacht ist. das komische ist.... ich hab ja auch en anderes projekt wo ich auch übern odbc reingegangen bin. das hat auch mal gegangen. geh jetzt auf dem pc auch net.

evtl odbc was falsch eingestellt.... mhhhh--- ich geh mal auf dem anderen pc testen...
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

Du sollst dir die DB auch nicht als Kopie in deiner Klasse speichern, sondern die statische Instanz benutzen. Würde klappen wenns in der gleichen Funktion ist, sieht man bei deinem Code aber nicht wo du das abfragst.

Und wenn du das dann tust, und deinen Queries trotzdem noch objQSqlDb mitgibst (was du nicht sollst), ist klar dass die dann sagen die DB wäre nicht offen.

Zusammenfassung:

a) Einmalig QSqlDatabase::addDatabase();
b) Einmalig QSqlDatabase::database().open();
c) Die Datenbank nicht als Kopie in deiner Klasse speichern
d) QSqlQuery q; (nicht QSqlQuery q(database); )
e) Database mit QSqlDatabase::database().isOpen(); prüfen (sollte aber unnötig sein)
AuE
Beiträge: 918
Registriert: 5. August 2008 10:58

Beitrag von AuE »

a) und b) sind klar. Sind im Konstruktor drin

wie meinst du das mit als Kopie speichern?
// ist eine provate klassenvariable
QSqlDatabase objQSqlDb; // in .h

weil wie soll ich sie sonst nutzen? Oder meinst ich soll sie als globale static anlegen?

Ich glaube Punkt d ist da nicht das Problem oder? Ich meine Ich sage ihm doch damit nur explizit nutze bitte die DB dafür. Wenn ich später mal mehr habe muss ich es ja dann eh machen (wenn)
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

Probier doch einfach mal, ob es so immer noch cores schmeisst:

Code: Alles auswählen

bool initDb(const QString& name, const QString& user, const QString& pass)
{
   QSqlDatabase db = QSqlDatabase::addDatabase(name);
   db.setUserName(user);
   db.setPassword(pass);

   return db.open();
}

int work()
{
   QSqlQuery q("select foo from bar");

   while (q.next())
      do();

   model->setQuery(q);

   return 0;
}
Antworten