Seite 1 von 2

[gelöst] Problem mit QAbstract Item Model

Verfasst: 17. November 2008 08:14
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

Verfasst: 17. November 2008 09:05
von upsala
Das interessantere daran wäre der Stack-Trace, da der Fehler in rowsAboutToBeRemoved nur noch ein Folgefehler zu sein scheint.

Re: Problem mit QAbstract Item Model

Verfasst: 17. November 2008 09:27
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.

Verfasst: 17. November 2008 09:38
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.

Verfasst: 17. November 2008 09:43
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?

Verfasst: 17. November 2008 10:15
von pfid
Sorry mein Fehler, du benutzt ein Query Model, das select() gibts bei den Table-Models. Zum refresehen sollte das setQuery() reichen.

Verfasst: 17. November 2008 10:35
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...

Verfasst: 17. November 2008 10:50
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.

Verfasst: 17. November 2008 10:57
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.....

Verfasst: 17. November 2008 11:25
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????

Verfasst: 17. November 2008 11:56
von pfid
QSqlDatabase::database().isOpen() gibt false zurück?

Verfasst: 17. November 2008 12:00
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...

Verfasst: 17. November 2008 12:07
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)

Verfasst: 17. November 2008 12:25
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)

Verfasst: 17. November 2008 12:35
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;
}