[gelöst] QMYSQL und cmake

Alles rund um die Programmierung mit Qt
d4eRc
Beiträge: 13
Registriert: 6. August 2010 16:54

[gelöst] QMYSQL und cmake

Beitrag von d4eRc »

Hallo zusammen,

ich habe folgendes Problem: Mein Programm soll eine Datenbankverbindung aufbauen; dazu verwende ich folg. Code:

Code: Alles auswählen

#include "trw.h"
#include <QtSql/QMYSQLDriver>

trw_mainwin::trw_mainwin()
{ ...
db_con = new QSqlDatabase;
db_con->addDatabase("QMYSQL");
connect_to_sql();
... }

void trw_mainwin::connect_to_sql() {
db_con->setHostName(sql_conf->server->text());
db_con->setPort(sql_conf->port->value());
db_con->setUserName(sql_conf->user->text());
db_con->setPassword(sql_conf->pass->text());
qDebug() << db_con->drivers();
if(!db_con->open()) {
  QMessageBox::warning(this,"SQL-Verbindung fehlgeschlagen","Die SQL-Verbindung konnte nicht aufgebaut werden:\n"+db_con->lastError().text());
  sql_conf->show();
  }
}
Als Fehlermeldung bringt mir die MessageBox jedoch "Driver not loaded" (x2). Da viele hier im Forum nur Probleme mit dem MySQL-Plugin unter Windows haben, verwirrt mich mein Problem etwas: Ich benutze openSuSE 11.3 und habe die QT4-devel-Pakete sowie das mysql-paket drauf (d.h. /usr/lib64/qt4/plugins/sqldrivers/libqsqlmysql.so existiert). Eine Idee war, dass das Problem vielleicht mit cmake zusammenhängt, da sonst viele das qmake verwenden (ich hab mich für cmake entschieden, weil KDE das ja verwendet und ich dessen Anwendung lernen wollte). Muss ich cmake irgendwelche Parameter außer

Code: Alles auswählen

find_package( Qt4 COMPONENTS QtCore QtGui QtSql REQUIRED )
angeben? Ich konnte bis jetzt nicht herausfinden, woher mein Programm später "wissen" soll, von wo es das MySql-Plugin laden soll bzw. ob ich das im Quellcode noch vermerken muss.
Probiert habe ich auch mal folgendes:

Code: Alles auswählen

QMYSQLDriver *driver = new QMYSQLDriver;
db_con->addDatabase(driver);
Das habe ich an das Beispiel in der Dokumentation von QSqlDatabase::addDatabase angelehnt, macht mir jedoch auch Probleme, weil der Linker schließlich eine Referenz auf QMYSQLDriver::QMYSQLDriver [...] nicht auflösen kann.
Das db_con->db_con->drivers() oben im Code spuckt mir

Code: Alles auswählen

("QSQLITE", "QMYSQL3", "QMYSQL")
aus.

Ich wäre zudem auch dankbar, wenn mir jemand eine Dokumentation zu der Bedeutung, Funktion und Anwendung von Plugins geben könnte

Im Voraus schonmal vielen Dank
Zuletzt geändert von d4eRc am 9. August 2010 00:05, insgesamt 1-mal geändert.
padreigh
Beiträge: 340
Registriert: 13. Mai 2010 10:06

Beitrag von padreigh »

http://lists.trolltech.com/qt-interest/ ... 924-0.html

vielleicht hilfts, warscheinlich eher nicht ... ganz unten ist ein cmake file das wohl tat ... damals
Patrick (QtCreator 1.3.1, Qt 4.6.3)
---
template = subdirs
d4eRc
Beiträge: 13
Registriert: 6. August 2010 16:54

Beitrag von d4eRc »

Der Unterschied zwischen meiner CMake-File und der von padreigh's link war ein ${QT_QTSQL_LIBRARIES} bei target_link_libraries, doch das macht keinen Unterschied, weil find_package(Qt4 ...QtSql ) die QTSQL-Lib schon in ${QT_LIBRARIES} packt.

Soweit ich weiß geht es ja mehr um das QMYSQL-Plugin, was nicht in direkter Verbindung mit der QtSql-Library steht. Das besondere am Plugin ist ja, falls ich richtig liege, dass das Programm nicht fest dagegen gelinkt wird, sondern die Library je nach Verfügbarkeit während der Laufzeit geladen wird. Demnach muss das Programm einen Pfad kennen, wo es nach dem Plugin sucht, was addDatabase("QMYSQL") dann vermutlich automatisch tun sollte - doch das tuts nicht.

Zudem frag ich mich, ob ich ein

Code: Alles auswählen

#include <QtSql/QMYSQLDriver>
brauche.
padreigh
Beiträge: 340
Registriert: 13. Mai 2010 10:06

Beitrag von padreigh »

reicht es dann evtl ein

/usr/share/qt4/plugins/sqldrivers

(da liegts bei mir, ubuntu) hinzuzufügen ?
Patrick (QtCreator 1.3.1, Qt 4.6.3)
---
template = subdirs
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

CMake hat nichs mit irgendwelchen nichtauffindbaren Plugins zu tun.
Wenn das MySQL-Plugin z.B. nicht installiert ist kann Qt es auch nicht laden. Ich schätze Du hast das Plugin gar nicht installiert.

Und <QtSql/QMYSQLDriver> ist überflüssig.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
d4eRc
Beiträge: 13
Registriert: 6. August 2010 16:54

Beitrag von d4eRc »

Woran kann ich denn erkennen, dass das Plugin nicht installiert ist? Wie oben steht, ist das entsprechende Paket installiert, die libqsqlmysql.so existiert und QSqlDatabase::drivers() listet QMYSQL mit auf.
Daraus schließe ich, dass das Plugin eigentlich installiert sein sollte. Kann ich anderweitig feststellen, warum das Laden fehlschlägt oder ob überhaupt ein Versuch unternommen wird, es zu laden?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Code: Alles auswählen

db_con = new QSqlDatabase;
db_con->addDatabase("QMYSQL");
connect_to_sql(); 
HMMM...
Lies dir mal die Beispiele durch, wie man sich so ein QSqlDatabase-Objekt erstellt.
Der Code ist einfach falsch, sry, db_con ist kein korrektes, funktionierendes QSqlDatabase-Objekt!
d4eRc
Beiträge: 13
Registriert: 6. August 2010 16:54

Beitrag von d4eRc »

db_con kommt aus meiner Klassendefinition:

Code: Alles auswählen

class trw_mainwin : public QWidget
{
Q_OBJECT
  public:
    trw_mainwin();
    QSqlDatabase *db_con;
...
};
Sonst würde der Kompiler das ganze ja nicht kompilieren :D
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

d4eRc hat geschrieben:Sonst würde der Kompiler das ganze ja nicht kompilieren :D
Nicht alles was der Compiler kompiliert, funktioniert. Denke an NULL-Pointer. Man kann sie ohne weiteres dereferenzieren . Compiler schweigt - aber es kracht halt dann beim Ausführen.
Wiederum andere Klassen sind ganz speziell in ihrer Verwendung konstruiert - wie QSqlDatabase. Schau dir doch mal GENAU in der Doku an, was addDatabase macht, wie (ganz genau!) es deklariert ist, dann kommst du sicher selber drauf.
Außerdem steht doch unter Detailed Description ein komplettes, funktionierendes Beispiel :(
d4eRc
Beiträge: 13
Registriert: 6. August 2010 16:54

Beitrag von d4eRc »

Nunja gut. Ich hab das Beispiel adaptiert, und das funktioniert:

Code: Alles auswählen

#include <QtSql>
int main()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("localhost");
db.setDatabaseName("stammat");
db.setUserName("trw");
db.setPassword("trw");
bool ok = db.open();
if(ok)
  qDebug() << "Erfolgreich geöffnet";
else
  qDebug() << "Fehler beim Öffnen:" << db.lastError().text();
}
Nur wie bekomm ich das jetzt hin, dass die QSqlDatabase ein Member meiner Fensterklasse ist, also dass ich darauf auch außerhalb meines Konstruktors (bspw. in einem Slot) zugreifen kann? Genauso interessiert mich, aus welchem Grund mein Code bis jetzt nicht funktioniert hat.
EDIT: Okay du hast ja schon recht genau beschrieben, warum es nicht getan hat, hab ich vorerst überlesen, tut mir leid. Wenn ich das also richtig verstehe, wird die QSqlDatabase-Klasse durch meine Definition in der Klasse nicht richtig instanziert und funktioniert deshalb auch nicht im weiteren Verlauf - doch warum ist gerade diese Klasse in ihrer Anwendung so speziell, wenn diese Vorgehensweise mit Buttons, Labels etc. gängige Praxis ist?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

d4eRc hat geschrieben:Nur wie bekomm ich das jetzt hin, dass die QSqlDatabase ein Member meiner Fensterklasse ist, also dass ich darauf auch außerhalb meines Konstruktors (bspw. in einem Slot) zugreifen kann?
QSqlDatabase hat einen default-Konstruktor, einen Kopierkonstruktor und einen Zuweisungsoperator. Du kannst also einfach "QSqlDatabase db;" als Member deklarieren und dann im Konstruktor ein passendes Objekt erstellen und und deinem Member zuweisen.

Aber noch besser wäre es in diesem Fall auf einen Member zu verzichten. Lies dir die Doku zu QSqlDatabase nochmal genauer durch. Du wirst auf "QSqlDatabase::addDatabase()" "QSqlDatabase::database()" und "connectionName" stoßen. Du kannst dir im Prinzip jederzeit ohne Objekt via QSqlDatabase::database() und passendem connetionName dein db-Objekt holen. QSqlDatabase verwaltet die ja alle für dich.
Genauso interessiert mich, aus welchem Grund mein Code bis jetzt nicht funktioniert hat.
"addDatabase" ist eine statische Funktion, das sagt dir (als Programmierer mit C++-Grundkenntnissen), dass dabei kein Objekt benötigt wird - statisch eben - und somit auch kein Objekt verändert werden kann, was du aber annimmst!
Ferner hat addDatabase einen Rückgabewert, und das auch noch vom Typ "QSqlDatabase"! Da muss es bimmeln :P
d4eRc
Beiträge: 13
Registriert: 6. August 2010 16:54

Beitrag von d4eRc »

Vielen dank, das macht Sinn. Dass addDatabase statisch ist, ist mir so garnicht aufgefallen. Man kann sich jetzt darüber streiten, ob "addDatabase" sich eher danach anhört, dass man der bestehenden QSqlDatabase eine Datenbank hinzufügt; doch wer lesen kann ist klar im Vorteil :) .
Wenn ich mit QSqlDatabase::database() arbeiten würde, müsste ich jedem Unterwidget meiner Fensterklasse doch ebendiese QSqlDatabase übergeben - verbraucht das nicht etwas viel Speicher? Ich habe vor, in mehreren Tabs meines Hauptfensters verschiedene QSqlTableModels anzulegen, die dieselbe QSqlDatabase verwenden. Kann es sein, dass man für jedes QSqlTableModel eine eigenständige QSqlDatabase braucht, d.h. database() muss ich sowieso verwenden? Oder könnte ich jeder Klasse einen Pointer auf die eine QSqlDatabase übergeben - oh da sehe ich dass QSqlTableModel keinen Pointer will, also müsste ich nochmal ein * vornanstellen, das geht sicher wieder schief :oops: -> also keine Pointer.

EDIT: Dazu fällt mir nochwas ein (hat mit meinem "Text" oben schon zu tun) - du sagst, dass QSqlDatabase alle Datenbankverbindungen für mich verwalten kann - doch wie kann ich mehr als eine Datenbank zu einer QSqlDatabase hinzufügen, wenn addDatabase() doch statisch ist?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

d4eRc hat geschrieben:doch warum ist gerade diese Klasse in ihrer Anwendung so speziell, wenn diese Vorgehensweise mit Buttons, Labels etc. gängige Praxis ist?
Weil eine Datenbankverbindung etwas anderes als ein Widget ist. Ein Widget (Label, Button, ...) hat seinen genau definierten Platz als child eines anderen Widgets, oder es ist ein Toplevel-Widget (kein parent). Es macht wenig Sinn, so ein Objekt im ganzen Programm ansprechbar zu halten (jedenfalls nicht per default).
Eine DB ist was ganz anderes. Wenn man ein Programm mit DB-Anbindung schreibt, werden sehr viele Klassen auf die DB angewiesen sein. Es wird recht holprig wenn man anfängt, die alle per Konstruktor/setter/... weiter zu reichen. Irgendwann wird sich jeder irgendwas eigenes basteln, um das zu erreichen. Schlimmstenfalls ein globales db-Objekt, oder eben eine freie Funktion oder eine statische Funktion einer anderen Klasse, oder...
Drum wird man sich gedacht haben "gut, machen wir das Speichern und holen der db-Verbindungen gleich von Haus aus leichter". (Bin aber kein Mitarbeiter von Trolltech, weiß also nicht ob noch andere tiefgreifendere Gründe ausschlaggebend waren - dass z.B. Objekte auch wirklich gelöscht werden und die Verbindung abgebaut, usw.)
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

QSqlDatabase ist sehr klein (wahrscheinlich nur ein einziger Pointer auf eine "QSqlDatabasePrivate"-Klasse). Kopieren geht ratzfatz - Pointer setzen fertig. Beim Kopieren wird mitgezählt, sodass der private-ptr nich gelöscht wird, obwohl das Objekt noch gebraucht wird.
Du hast keine andere Chance als dem Model eine Kopie zu geben, tricksen mit Pointer verbietet dir der Compiler :P
Der "connectionName" ist der Schlüssel zu mehr als einer db-Verbindung:

Code: Alles auswählen

QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
// db aufsetzen
// db ist jetzt unsere "Default-Connection"

db = QSqlDatabase::addDatabase("QMYSQL", "mysql-connection");
// db aufsetzen
// db hat nicht das alte Objekt gelöscht, sondern ist eine Kopie eines neuen Objektes

// Zugriffe:
QSqlDatabase db = QSqlDatabase::database("mysql-connection");
// db "zeigt" jetzt auf die mysql-db
db = QSqlDatabase::database();
// db "zeigt" auf die default-connection
Hoffe das ist jetzt klarer.
d4eRc
Beiträge: 13
Registriert: 6. August 2010 16:54

Beitrag von d4eRc »

Danke für deine Antwort. Vielleicht hast du den Zusatz in meinem letzten Post noch nicht gelesen, diese zwei Fragen würden mich aber trotzdem interessieren:
- (von oben) Du sagst, dass QSqlDatabase alle Datenbankverbindungen für mich verwalten kann - doch wie kann ich mehr als eine Datenbank zu einer QSqlDatabase hinzufügen, wenn addDatabase() doch statisch ist?
- Wenn ich das jetzt richtig verstanden habe, soll ich für jedes neue Objekt meine QSqlDatabase mit database() "klonen" und den verschiedenen Klassen übergeben - das macht auch sicher keine Probleme mit der eigentlichen Netzwerkverbindung zum Server (wegen vielen Verbindungen)? Denn ich denk mir dass die einzelnen QSqlDatabase-Objekt meiner Unterfenster jeweis ihr eigenes open() brauchen und demnach jedes eine einzelne Verbindung aufbaut, oder reicht das einmal in der Klasse im Konstruktor und übernehmen die Objekte die database() zurückgibt den geöffneten Status des "Mutterobjekts"?

EDIT: Okay du hast schon alles beantwortet :) Du warst mir echt eine große Hilfe, dankeschön!
Antworten