Creating common database connection for sevaral forms using qt c++ - c++

I am creating a simple qt application to provide details and login to the application and retrieve details from the database.It has mainly 2 forms(MainWindow and Dialog) A DBconncetion class has been written to obtain a database connection!
I used the DBconnection class to log into application by giving details via the MainWindow form! but I don't know how to keep the connection that I opened in the MainWindow form and use it to retreive the data to the tableview in the Dialog form.
mycode is as follows
DBconnection.h (working successfully)
public:
QSqlDatabase mydb;
bool connOpen(QString uname,QString pword,QString ip,int port,QString dbname){
mydb=QSqlDatabase::addDatabase("QOCI","MyDB");
mydb.setUserName(uname);
mydb.setPassword(pword);
mydb.setHostName(ip);
mydb.setPort(port);
mydb.setDatabaseName(dbname);
mydb.open();
return true;
}
MainWindow.cpp (working successfully)
void MainWindow::on_pushButton_clicked()
{
DBconnection con;
if(con.connOpen(ui->lineEdit->text(),ui->lineEdit_2->text(),ui->lineEdit_3->text(),ui->lineEdit_4->text().toInt(),ui->lineEdit_5->text())){
Dialog dialog1;
dialog1.setModal(true);
dialog1.exec();
}
}
Dialog.cpp (not working)
void Dialog::on_pushButton_clicked()
{
QSqlQueryModel *modal = new QSqlQueryModel();
con.connOpen();
QSqlQuery* qry=new QSqlQuery(con.mydb);
qry->prepare("select NAME FROM TEST1");
qry->exec();
modal->setQuery(*qry);
ui->tableView->setModel(modal);
}
How can I adjust my code so that I can retrieve data to the tablewidget in Dialog form from the connection I made from the MainWindow form?

You could either pass a reference to the connection to your dialog, or make the connection static/global.
e.g.1
class Dialog()
{
DBconnection &con;
Dialog(DBconnection &con) : con(con) {};
};
Instead of a reference, you might also want to use a std::shared_ptr.
A nice way of making the connection global/static would be through the Service locator pattern. This pattern uses a central registry known as the "service locator", which on request returns the information necessary to perform a certain task.
You might also want to have a look into things related to "Dependency injection"

Related

QT Creator C++ : Passing information from QDialog to MainWindow

I'm trying to make a program with the following:
In MainWindow (QMainWindow), I have a button AddUser that's opens a secondary window (QDialog) where I have 3 spaces to write the name, email and mobile number of user to add to program.
I want that, after introduce all those information, I click in Add button and the window closes and the information I wrote goes to a vector of User (vector<User> users) located in MainWindow so I can use it.
I have all of this stuff done, just the passing information I can't do.
I already searched about it but I just found make a login window (secondary window opens before main window and after introduce data it closes the login window and open the main window with that information saved). I want basically that but the difference is that secondary window opens when I click in a button in MainWindow
But it's not working well, can someone help me?
I have this code (based on Login context code):
adduserwindow.h (secondary window)
signals:
void add(const User & user);
adduserwindow.cpp
void AddUserWindow::on_button_addUser_clicked() // Add button after write the info
{
QString name = ui->text_name->text();
QString email = ui->text_email->text();
QString mobile = ui->text_mobile->text();
User u1(name.toStdString(),email.toStdString(),mobile.toStdString());
users_.push_back(u1);
emit add(u1);
}
mainwindow.h
public:
void setUser(const User &user);
private:
User mUser;
mainwindow.cpp
void MainWindow::on_button_adduser_clicked() // AddUser button in MainWindow
{
AddUserWindow adduser_window(this);
adduser_window.exec();
QObject::connect(&adduser_window, &AddUserWindow::add, [this](const User user) {
this->setUser(user);
this->show();
});
}
void MainWindow::setUser(const User& user)
{
mUser = user;
qDebug()<<mUser.toString(); //toString() is a method of User class to convert std::string to QString
}
Obs: I have this at the end of User.h:
Q_DECLARE_METATYPE(User)
Just to leave an answer for future visitors... The issue here was that QDialog's exec() function does not return until the user closes the dialog. In this case the simple solution is to make any signal connections before calling exec().
However, its documentation recommends using open(), or alternatively show() for modeless dialogs. These functions both return immediately, so the dialog's lifetime would need to be tied to its parent window, by giving it dynamic storage duration:
void MainWindow::on_button_adduser_clicked() // AddUser button in MainWindow
{
auto* adduser_window = new AddUserWindow(this);
QObject::connect(adduser_window, &AddUserWindow::add, [this](const User user) {
this->setUser(user);
this->show();
});
adduser_window.open();
}
That is the idiomatic Qt way of doing it and I would recommend it, because it works whether or not the dialog (or other QObject) will actually outlive the function.

QSqlTableModel: change database after constructing

I'm creating an application which uses QSqlDatabase and QSqlTableModel for inserting and retaining data from an SQLite database file.
The database instance is being created in MyApplication:
MyApplication.h
#include <QSqlDatabase>
// ...
class MyApplication
{
public:
// ....
private:
QSqlDatabase _database;
};
MyApplication.cpp
MyApplication::start()
{
// ...
_database = QSqlDatabase::database();
// ...
}
For data model handling I'm using an overloaded class of QSqlTableModel:
SqlContactModel.h
#include <QSqlTableModel>
class SqlContactModel : public QSqlTableModel
{
public:
// ...
void setDatabase(const QSqlDatabase& database) { _database = database };
private:
QSqlDatabase _database;
};
SqlContactModel overloads the typical methods such as data(), roleNames() etc.
My instance of SqlContactModel is used in QML, thus I'm creating the instance in my main.cpp as follows:
main.cpp
int main()
{
// ...
qmlRegisterType<SqlContactModel>("io.taibsu.qxmt", 1, 0, "SqlContactModel");
}
Now since it's being created via qmlRegisterType, I can't pass any parameters in the constructor to set a different database in the SqlContactModel.
I need to pass a different database to it since I'm using multiple different overloaded QSqlTableModel classes (e.g., the other class is called SqlConversationModel and shall use the same _database instance which is used in MyApplication).
Now there are two ways to solve this: either pass the database somehow to my SqlTableModel subclasses via QML or find another way to tell the TableModel to not use its own database instance but the one already there.
Now my questions are:
How can I use a single database in different table models?
Is there a way to pass parameters when constructing a class instance in QML?
The best way probably is to set up your database from a controller class in C++, give that controller a (read only & constant) properly contactsModel and expose the controller as a singleton to QML. This way you can setup the database from C++ (where your business logic belongs) while you can access your data from QML. There probably isn’t even a need to have that property be another type than QAbstractItemModel* so you keep the freedom to modify it to another model type if needed in the future without affecting your QML. Such decoupling is desirable.

How to add hyperlinks in Qt without QLabel?

I have some labels and layouts nested inside a QWidget to build a part of a sidebar. Each QWidget is its own section and one component currently looks like this:
To my understanding, you can only set hyperlinks with QLabel, but I'm trying to get the whole area between the white lines clickable. This is including the icon and the whitespace. Is there any way to achieve this?
This got marked as a duplicate to the opposite of what I was asking, so I'd like to reiterate that I'm trying to implement a hyperlink without QLabel.
You can easily have a widget open a link on click:
class Link : public QWidget {
Q_OBJECT
public:
Link(QUrl url, QWidget p = nullptr) : QWidget(p), _url(url) {}
QUrl _url;
void mouseReleaseEvent(QMouseEvent *) { QDesktopServices::openUrl(_url); }
}
You can avoid any extra signals and connections, and have each link widget store its own link internally, the url can be set on construction and changed at any time. Not using signals and slots makes it easier to change the link too, without having to disconnect previous connections.
IMO going for a signals and slots solution is only justified when you want different arbitrary behavior. In this case you always want the same - to open a particular link, so you might as well hardcode that and go for an easier and more computationally efficient solution.
I would just manually catch the SIGNAL for clicked() and use desktop services to open the url in code.
bool QDesktopServices::openUrl ( const QUrl & url ) [static]
Opens the given url in the appropriate Web browser for the user's desktop environment, and returns true if successful; otherwise returns false.
http://doc.qt.io/qt-4.8/signalsandslots.html
Using this type of syntax, or in the designer, you can also connect a signal to a slot.
connect(widgetThatRepresentsURL, SIGNAL(clicked()),
handlerThatWillOpenTheURL, SLOT(clicked_on_url()));
For widgets that don't have a signal set up for clicked (or whatever event you are interested in), you can subclass the widget in question and reimplement...
void QWidget::mousePressEvent ( QMouseEvent * event ) [virtual protected]
Specifically for creating a signal, there is emit. I've used this in the past like the following
void Cell::focusInEvent(QFocusEvent *e)
{
emit focus(this, true);
QLineEdit::focusInEvent(e);
}
with the following in the header
signals:
void focus(Cell *, bool);

c++/Qt: How to get a reference to database in the MainWindow class?

In Qt-creator, I created SQLite database in a class called databaseManager, as follow:
QString DatabaseManager::open_db()
{
QSqlDatabase db;
QString path = "/Users/me/Documents/workspace/Muasaa/";
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(path+"Database v.1");
if (db.open()){
return "Database is created, open, and ready ...";
} else {
return db.lastError().text();
}
}
Then I define the following in the header file of the MainWindow class:
Public:
DatabaseManager *db_manager;
In the source file, I call it as follow:
db_manager->open_db();
which creates and open the database.
However, I would like to use a reference to same database to use it in many functions in the MainWindow source file. How can I do that ?!
Move the QSqlDatabase db variable into your class header, and add a method for getting it. As long as you create an instance of your databaseManager class and maintain a pointer to it in MainWindow you'll be able to retrieve it.
QSqlDatabase::database() is a static function that returns a QSqlDatabase.
If you have more than one database, you have to provide a connection name in addDatabase() and in database()
Maybe a design solution may help?
Whatever your goal is, consider turning your DatabaseManager class into a Singleton. This way you'll be able to use your manager in all the gui classes. Something like DatabaseManager::instance()->your_method()
Advantages:
You are sure the connections are managed the right way
Less oportunities for build problems
By the way, I'm not sure, but your program crash may be the result of using your db_manager pointer before it was initialized, in a slot maybe, or (more probable) it's an internal connection error. Have you checked the same connection attributes in a minimum possible example?

where should I declare a database object

I am using the Qt libraries in a C++ project but I have a design question: where should a database be declared? I would prefer not to declare global variables.
Currently I am dealing with this problem in this way. I have a mainwindow and I have declared the DB in there so I perform the queries in the main window and pass the results to the dialogs using different signals and slots.
I start the DB when the main window starts and close it when the window has been closed. I don't know if this is ok
Now I need the DB connection in another class as well so I can pass a reference to the DB or make the DB global
I don't like these solutions.. is there a standard pattern to deal with this situation?
edit:
My class now looks like:
class Database
{
public:
bool open(void);
bool close(void);
static Database* getDatabase(void);
// various methods like loadThisTable(), saveThisTable() etc
private:
Database(); // disable constructor
~Database(); // disable destructor
Database(const Database&); // disable copy constructor
Database& operator=(const Database&); // disable assignment
static Database* instance_; // database instance
QSqlDatabase qtDB; // qt db database
}
If I want I can add the add and remove methods but I have a single DB instance.
If you're using QSqlDatabase, you don't really need to make it a global variable. Just set up the connection when you first start your application, then use the static QSqlDatabase methods to access the connection when you need it in different modules.
Example
QSqlDatabase db; // set up the default connection
// alternative: set up a named connection
// QSqlDatabase db("conn-name");
// set the connection params and open the connection
// ... later on
QSqlDatabase db = QSqlDatabase::database(); // retrieve the default connection
// alternative: retrieve the named connection
// QSqlDatabase db = QSqlDatabase::database("conn-name");
From the docs:
QSqlDatabase is a value class. Changes made to a database connection via one instance of QSqlDatabase will affect other instances of QSqlDatabase that represent the same connection. Use cloneDatabase() to create an independent database connection based on an existing one.
Note: If you're application is multi-threaded, you have to be careful to only use a connection in the thread in which it was created.
You need a singleton pattern. It's a global class which have only one instance. Someone calls it antipattern (and sometimes it is), but it is the best way to handle resources like database connections. And dont forget that you can use QSqlDatabase QSqlDatabase::database ( const QString & connectionName = QLatin1String( defaultConnection ), bool open = true ) [static] method to get QSqlDatabase instance by name (name can be set via QSqlDatabase QSqlDatabase::addDatabase ( QSqlDriver * driver, const QString & connectionName = QLatin1String( defaultConnection ) ) [static] method) to avoid creating singleton just for storing QSqlDatabase instances.