commit ad0d38876b11e4986ff6a337501c59cc7959c537 Author: Andrzej Wójtowicz Date: Fri Sep 1 22:53:26 2017 +0200 Added project files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cab62ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# C++ objects and libs + +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so +*.dll +*.dylib + +# Qt-es + +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +Makefile* +*build-* + +build/* + +# QtCreator + +*.autosave + +# QtCtreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCtreator CMake +CMakeLists.txt.user* + diff --git a/README.md b/README.md new file mode 100644 index 0000000..c021689 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# QT SQL Example + +An exemplary Qt app that connects to SQL server and displays a table from a database. The program was made for educational purposes. + +![](screenshot.png) + +The program was tested on: + * client: Windows 10, Qt 5.9.1, + * servers: MySQL Server 5.5.57, Microsoft SQL Server 2008 R2 SP3. diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..dde3b15 --- /dev/null +++ b/config.ini @@ -0,0 +1,9 @@ +[sql] +engine=mssql +driver=SQL Server +address=localhost\\SQLEXPRESS +port=1433 +authentication=server +login=user +password=password +database=db diff --git a/qt-sql-example.pro b/qt-sql-example.pro new file mode 100644 index 0000000..cc93e9c --- /dev/null +++ b/qt-sql-example.pro @@ -0,0 +1,22 @@ +VERSION = 1.0 +QMAKE_TARGET_DESCRIPTION = "Qt app that displays SQL table; for educational purposes" +QMAKE_TARGET_COPYRIGHT = "Andrzej Wojtowicz, Adam Mickiewicz University in Poznan" +QMAKE_TARGET_PRODUCT = "Qt SQL Example" + +QT += core gui sql widgets + +TARGET = qt-sql-example +TEMPLATE = app + +SOURCES += src/main.cpp\ + src/mainwindow.cpp \ + src/db_controller.cpp + +HEADERS += src/mainwindow.h \ + src/db_controller.h + +FORMS += src/mainwindow.ui + +RESOURCES += res/icons.qrc + +win32:RC_ICONS += res/database.ico diff --git a/res/database.ico b/res/database.ico new file mode 100644 index 0000000..f17d6e7 Binary files /dev/null and b/res/database.ico differ diff --git a/res/failure.ico b/res/failure.ico new file mode 100644 index 0000000..38ff818 Binary files /dev/null and b/res/failure.ico differ diff --git a/res/icons.qrc b/res/icons.qrc new file mode 100644 index 0000000..4e5b607 --- /dev/null +++ b/res/icons.qrc @@ -0,0 +1,7 @@ + + + database.ico + failure.ico + success.ico + + diff --git a/res/readme.txt b/res/readme.txt new file mode 100644 index 0000000..fb529d0 --- /dev/null +++ b/res/readme.txt @@ -0,0 +1,5 @@ +database.ico: +- author: http://barrymieny.deviantart.com +- license: Creative Commons Attribution Non-commercial Share Alike (by-nc-sa) +success.ico & failure.ico +- author: https://www.iconfinder.com/iconpack \ No newline at end of file diff --git a/res/success.ico b/res/success.ico new file mode 100644 index 0000000..fb219e3 Binary files /dev/null and b/res/success.ico differ diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..9062ae5 Binary files /dev/null and b/screenshot.png differ diff --git a/src/db_controller.cpp b/src/db_controller.cpp new file mode 100644 index 0000000..ffc88f8 --- /dev/null +++ b/src/db_controller.cpp @@ -0,0 +1,139 @@ +#include "db_controller.h" + +DbController::DbController(QObject* parent) : + QObject(parent) +{ + +} + +DbController::~DbController() +{ + if (db.isOpen()) + db.close(); +} + +void DbController::connectToServerRequested(QString engine, QString driver, QString server, int port, QString database, + QString login, QString password, bool is_sql_authentication) +{ + db = QSqlDatabase(); + db.removeDatabase("example-connection"); // remove old connection if exists + + if (engine == "mysql") + { + db = QSqlDatabase::addDatabase("QMYSQL", "example-connection"); + } + else if (engine == "mssql") + { + db = QSqlDatabase::addDatabase("QODBC", "example-connection"); + } + else + { + emit serverErrorWithConnection("Unknown database engine"); + return; + } + + bool connection_succesfull; + + if (engine == "mysql") + { + connection_succesfull = connectToServerMySQL(server, port, database, login, password); + } + else if (engine == "mssql") + { + connection_succesfull = + (is_sql_authentication ? connectToServerMSSQL(driver, server, port, database, login, password) : + connectToServerMSSQL(driver, server, port, database)); + } + else + { + emit serverErrorWithConnection("Unknown database engine"); + return; + } + + if (connection_succesfull) + emit serverConnected(); + else + emit serverErrorWithConnection(getLastError().driverText()); +} + +void DbController::disconnectFromServerRequested() +{ + disconnectFromServer(); + + emit serverDisconnected(); +} + +bool DbController::checkIfTableExists(QString table) +{ + return db.tables().contains(table); +} + +bool DbController::checkIfConnected() +{ + return db.isOpen(); +} + +void DbController::selectTableRequested(QString table) +{ + QSqlQueryModel* model = selectTable(table); + + emit tableSelected(model); +} + +void DbController::getTablesNamesRequested() +{ + emit gotTablesNames(db.tables()); +} + +bool DbController::connectToServerMSSQL(QString driver, QString server, int port, QString database, + QString login, QString password) +{ + db.setDatabaseName(connection_string_sqlauth.arg(driver).arg(server).arg(port).arg(database) + .arg(login).arg(password)); + + return db.open(); +} + +bool DbController::connectToServerMSSQL(QString driver, QString server, int port, QString database) +{ + db.setDatabaseName(connection_string_winauth.arg(driver).arg(server).arg(port).arg(database)); + + return db.open(); +} + +bool DbController::connectToServerMySQL(QString server, int port, QString database, + QString login, QString password) +{ + db.setHostName(server); + db.setPort(port); + db.setDatabaseName(database); + db.setUserName(login); + db.setPassword(password); + + return db.open(); +} + +void DbController::disconnectFromServer() +{ + db.close(); +} + +QSqlQueryModel* DbController::selectTable(QString name) +{ + QSqlQueryModel* model = new QSqlQueryModel; + + model->setQuery("SELECT * FROM " + name, db); + + return model; +} + +QSqlError DbController::getLastError() +{ + return db.lastError(); +} + +const QString DbController::connection_string_sqlauth = + QString("DRIVER={%1};SERVER=%2;PORT=%3;DATABASE=%4;UID=%5;PWD=%6"); + +const QString DbController::connection_string_winauth = + QString("DRIVER={%1};SERVER=%2;PORT=%3;DATABASE=%4"); diff --git a/src/db_controller.h b/src/db_controller.h new file mode 100644 index 0000000..6ab257e --- /dev/null +++ b/src/db_controller.h @@ -0,0 +1,42 @@ +#ifndef DB_CONTROLLER_H +#define DB_CONTROLLER_H + +#include +#include + +class DbController : public QObject +{ + Q_OBJECT +public: + explicit DbController(QObject*); + ~DbController(); + bool checkIfTableExists(QString); + bool checkIfConnected(); + +public slots: + void connectToServerRequested(QString, QString, QString, int, QString, QString, QString, bool); + void disconnectFromServerRequested(); + void selectTableRequested(QString); + void getTablesNamesRequested(); + +signals: + void serverConnected(); + void serverErrorWithConnection(QString); + void serverDisconnected(); + void tableSelected(QSqlQueryModel*); + void gotTablesNames(QStringList); + +private: + bool connectToServerMSSQL(QString, QString, int, QString, QString, QString); + bool connectToServerMSSQL(QString, QString, int, QString); + bool connectToServerMySQL(QString, int, QString, QString, QString); + void disconnectFromServer(); + QSqlQueryModel* selectTable(QString); + QSqlError getLastError(); + + QSqlDatabase db; + static const QString connection_string_sqlauth; + static const QString connection_string_winauth; +}; + +#endif // DB_CONTROLLER_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..cecf3d1 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,21 @@ +#include "db_controller.h" +#include "mainwindow.h" + +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + DbController db_controller(0); + QThread db_thread; + + db_controller.moveToThread(&db_thread); + db_thread.start(); + + MainWindow window(0, &db_controller, &db_thread); + window.show(); + + return app.exec(); +} diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp new file mode 100644 index 0000000..585198d --- /dev/null +++ b/src/mainwindow.cpp @@ -0,0 +1,288 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget* parent, DbController* dbc, QThread* dbt) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + db_controller = dbc; + db_thread = dbt; + + // check Qt SQL drivers + + QStringList sql_drivers = QSqlDatabase::drivers(); + ui->label_qmysql_icon->setPixmap( + QPixmap(":/icons/" + QString(sql_drivers.contains("QMYSQL") ? "success" : "failure") + ".ico")); + ui->label_qodbc_icon->setPixmap( + QPixmap(":/icons/" + QString(sql_drivers.contains("QODBC") ? "success" : "failure") + ".ico")); + + ui->radio_mysql->setEnabled(sql_drivers.contains("QMYSQL")); + ui->radio_mssql->setEnabled(sql_drivers.contains("QODBC")); + + if (!sql_drivers.contains("QMYSQL") && !sql_drivers.contains("QODBC")) + ui->groupBox_sql_connect->setEnabled(false); + + // connect signals with slots + + // ui => ui + + connect(ui->button_connect, SIGNAL(clicked()), this, SLOT(connectToServerRequested())); + connect(ui->radio_mssql, SIGNAL(clicked()), this, SLOT(engineChanged())); + connect(ui->radio_mysql, SIGNAL(clicked()), this, SLOT(engineChanged())); + connect(ui->radio_sql_authentication, SIGNAL(clicked()), this, SLOT(authenticationMethodChanged())); + connect(ui->radio_windows_authentication, SIGNAL(clicked()), this, SLOT(authenticationMethodChanged())); + connect(ui->button_show_table, SIGNAL(clicked()), this, SLOT(showTableRequested())); + + // ui => db_controller + + connect(this, SIGNAL(connectToServer(QString,QString,QString,int,QString,QString,QString,bool)), + db_controller, SLOT(connectToServerRequested(QString,QString,QString,int,QString,QString,QString,bool))); + connect(this, SIGNAL(disconnectFromServer()), db_controller, SLOT(disconnectFromServerRequested())); + connect(this, SIGNAL(selectTable(QString)), db_controller, SLOT(selectTableRequested(QString))); + connect(this, SIGNAL(getTablesNames()), db_controller, SLOT(getTablesNamesRequested())); + + // db_controller => ui + + connect(db_controller, SIGNAL(serverConnected()), this, SLOT(serverConnected())); + connect(db_controller, SIGNAL(serverErrorWithConnection(QString)), + this, SLOT(serverErrorWithConnection(QString))); + connect(db_controller, SIGNAL(serverDisconnected()), this, SLOT(serverDisconnected())); + connect(db_controller, SIGNAL(tableSelected(QSqlQueryModel*)), this, SLOT(displayTable(QSqlQueryModel*))); + connect(db_controller, SIGNAL(gotTablesNames(QStringList)), this, SLOT(fillTablesNames(QStringList))); + + // load settings + + QString inifile("config.ini"); + QFileInfo check_file(inifile); + if (check_file.exists() && check_file.isFile()) + { + QSettings settings(inifile, QSettings::Format::IniFormat); + + QString engine = settings.value("sql/engine", "").toString(); + if (engine == "mysql") + { + ui->radio_mysql->setChecked(true); + ui->radio_windows_authentication->setEnabled(false); + ui->lineEdit_driver->setEnabled(false); + } + else if (engine == "mssql") + { + ui->radio_mssql->setChecked(true); + } + + ui->lineEdit_driver->setText(settings.value("sql/driver", "").toString()); + ui->lineEdit_server_address->setText(settings.value("sql/address", "").toString()); + ui->spinBox_server_port->setValue(settings.value("sql/port", 0).toInt()); + QString auth = settings.value("sql/authentication", "").toString(); + ui->radio_sql_authentication->setChecked(auth == "server" && (engine == "mssql" || engine == "mysql")); + ui->radio_windows_authentication->setChecked(auth == "windows" && engine == "mssql"); + ui->lineEdit_login->setText(settings.value("sql/login", "").toString()); + ui->lineEdit_password->setText(settings.value("sql/password", "").toString()); // plain text, so secure... + ui->lineEdit_database_name->setText(settings.value("sql/database", "").toString()); + + ui->statusBar->showMessage("Settings file config.ini loaded", 3000); + } + else + { + ui->statusBar->showMessage("Settings file config.ini does not exist", 5000); + } + + +} + +MainWindow::~MainWindow() +{ + db_thread->exit(); + db_thread->wait(); + delete ui; +} + +void MainWindow::connectToServerRequested() +{ + QString engine; + if (ui->radio_mysql->isChecked()) + engine = "mysql"; + else if (ui->radio_mssql->isChecked()) + engine = "mssql"; + else + { + QMessageBox::information(this, + "Invalid Engine", + "Choose database engine", + QMessageBox::Ok); + return; + } + + QString driver = ui->lineEdit_driver->text(), + server = ui->lineEdit_server_address->text(), + database = ui->lineEdit_database_name->text(), + login = ui->lineEdit_login->text(), + password = ui->lineEdit_password->text(); + int port = ui->spinBox_server_port->value(); + + if (server == "") + { + QMessageBox::information(this, + "Invalid Connection Data", + "Insert server address to connect", + QMessageBox::Ok); + return; + } + + bool is_sql_authentication = ui->radio_sql_authentication->isChecked(); + + if (is_sql_authentication && login == "") + { + QMessageBox::information(this, + "Invalid Connection Data", + "Insert login to connect", + QMessageBox::Ok); + return; + } + + if (database == "") + { + QMessageBox::information(this, + "Invalid Connection Data", + "Insert database name to connect", + QMessageBox::Ok); + return; + } + + ui->button_connect->setEnabled(false); + ui->statusBar->showMessage("Connecting..."); + + emit connectToServer(engine, driver, server, port, database, login, password, is_sql_authentication); +} + +void MainWindow::disconnectFromServerRequested() +{ + ui->button_connect->setEnabled(false); + + emit disconnectFromServer(); +} + +void MainWindow::authenticationMethodChanged() +{ + bool is_sql_authentication = ui->radio_sql_authentication->isChecked(); + + ui->lineEdit_login->setEnabled(is_sql_authentication); + ui->lineEdit_password->setEnabled(is_sql_authentication); +} + +void MainWindow::engineChanged() +{ + bool is_mssql_engine = ui->radio_mssql->isChecked(); + + ui->lineEdit_driver->setEnabled(is_mssql_engine); + ui->radio_windows_authentication->setEnabled(is_mssql_engine); + + ui->spinBox_server_port->setValue(is_mssql_engine ? 1433 : 3306); + + if (!is_mssql_engine) + { + ui->radio_sql_authentication->setChecked(true); + emit authenticationMethodChanged(); + } +} + +void MainWindow::showTableRequested() +{ + ui->button_show_table->setEnabled(false); + + QString table_name = ui->comboBox_table_name->currentText(); + + emit selectTable(table_name); +} + +void MainWindow::serverConnected() +{ + ui->button_connect->setEnabled(true); + + disconnect(ui->button_connect, SIGNAL(clicked()), this, SLOT(connectToServerRequested())); + connect(ui->button_connect, SIGNAL(clicked()), this, SLOT(disconnectFromServerRequested())); + + ui->button_connect->setText("Disconnect"); + ui->groupBox_database_browser->setEnabled(true); + + ui->statusBar->showMessage("Connected", 3000); + + emit getTablesNames(); +} + +void MainWindow::fillTablesNames(QStringList tables_names) +{ + if (tables_names.length() == 0) + QMessageBox::warning(this, + "Tables", + "There are no tables to display in the database", + QMessageBox::Ok); + else + { + ui->comboBox_table_name->addItems(tables_names); + + ui->comboBox_table_name->setEnabled(true); + ui->comboBox_table_name->setFocus(); + } +} + +void MainWindow::serverErrorWithConnection(QString message) +{ + QMessageBox::critical(this, + "Connection failed", + message, + QMessageBox::Ok); + + ui->button_connect->setEnabled(true); + + ui->statusBar->showMessage("Connection failed", 3000); +} + +void MainWindow::serverDisconnected() +{ + disconnect(ui->button_connect, SIGNAL(clicked()), this, SLOT(disconnectFromServerRequested())); + connect(ui->button_connect, SIGNAL(clicked()), this, SLOT(connectToServerRequested())); + + ui->tableView_database_table->setModel(NULL); + + ui->button_connect->setEnabled(true); + ui->button_connect->setText("Connect"); + + ui->comboBox_table_name->clear(); + ui->comboBox_table_name->setEnabled(false); + + ui->groupBox_database_browser->setEnabled(false); + ui->button_connect->setFocus(); +} + +void MainWindow::displayTable(QSqlQueryModel* model) +{ + if (!model->lastError().isValid()) + ui->tableView_database_table->setModel(model); + else + QMessageBox::critical(this, + "Select failed", + model->lastError().databaseText(), + QMessageBox::Ok); + + ui->button_show_table->setEnabled(true); + ui->comboBox_table_name->setFocus(); +} + +void MainWindow::keyPressEvent(QKeyEvent* pe) +{ + if (pe->key() == Qt::Key_Enter || pe->key() == Qt::Key_Return) + { + if (!db_controller->checkIfConnected()) + emit connectToServerRequested(); + else if (ui->comboBox_table_name->isEnabled() && ui->comboBox_table_name->hasFocus()) + emit showTableRequested(); + } +} diff --git a/src/mainwindow.h b/src/mainwindow.h new file mode 100644 index 0000000..796c5ea --- /dev/null +++ b/src/mainwindow.h @@ -0,0 +1,49 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "db_controller.h" + +#include + +namespace Ui +{ + class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget* parent, DbController* dbc, QThread* dbt); + ~MainWindow(); + +public slots: + void connectToServerRequested(); + void disconnectFromServerRequested(); + void engineChanged(); + void authenticationMethodChanged(); + void showTableRequested(); + + void serverConnected(); + void serverErrorWithConnection(QString); + void serverDisconnected(); + void displayTable(QSqlQueryModel*); + void fillTablesNames(QStringList); + +signals: + void connectToServer(QString, QString, QString, int, QString, QString, QString, bool); + void disconnectFromServer(); + void selectTable(QString); + void getTablesNames(); + +private: + Ui::MainWindow* ui; + DbController* db_controller; + QThread* db_thread; + +protected: + virtual void keyPressEvent(QKeyEvent*); +}; + +#endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui new file mode 100644 index 0000000..082b840 --- /dev/null +++ b/src/mainwindow.ui @@ -0,0 +1,556 @@ + + + MainWindow + + + + 0 + 0 + 690 + 483 + + + + + 0 + 0 + + + + + 690 + 483 + + + + Qt SQL Example + + + + :/icons/database.ico:/icons/database.ico + + + + + + + + + + 0 + 0 + + + + + 250 + 45 + + + + Qt SQL database drivers + + + + + 53 + 23 + 13 + 13 + + + + + + + :/icons/failure.ico + + + + + + 143 + 23 + 13 + 13 + + + + + + + :/icons/failure.ico + + + + + + 73 + 22 + 47 + 13 + + + + QMYSQL + + + + + + 163 + 22 + 47 + 13 + + + + QODBC + + + + + + + + + 0 + 0 + + + + + 250 + 390 + + + + + 50 + false + + + + Connect to server + + + false + + + false + + + + + 10 + 175 + 231 + 141 + + + + Authentication + + + + true + + + + 90 + 54 + 131 + 21 + + + + + + + + + + 10 + 24 + 151 + 17 + + + + + 0 + 0 + + + + SQL Server authentication + + + true + + + + + + 10 + 114 + 141 + 17 + + + + + 0 + 0 + + + + Windows authentication + + + false + + + + + true + + + + 90 + 84 + 131 + 20 + + + + + + + QLineEdit::Password + + + + + + 30 + 54 + 51 + 16 + + + + login: + + + + + + 30 + 84 + 61 + 16 + + + + password: + + + lineEdit_login + radio_sql_authentication + radio_windows_authentication + lineEdit_password + label + label_2 + groupBox_sql_drivers + + + + + 88 + 356 + 75 + 23 + + + + Connect + + + true + + + + + + 10 + 115 + 41 + 21 + + + + server: + + + + + + 100 + 115 + 141 + 20 + + + + + + + + + + 100 + 326 + 141 + 20 + + + + + + + + + + 10 + 326 + 81 + 20 + + + + database name: + + + + + + 10 + 145 + 51 + 20 + + + + port: + + + + + + 100 + 145 + 61 + 22 + + + + Default: MySQL 3306; MSSQL 1433 + + + 65535 + + + 0 + + + + + + 10 + 85 + 41 + 21 + + + + driver: + + + + + + 100 + 85 + 141 + 20 + + + + For ODBC/MSSQL: "SQL Server" (Windows), "ODBC Driver 13 for SQL Server", "FreeTDS" (unix), etc. + + + + + + + + + 10 + 20 + 231 + 51 + + + + Engine + + + + + 135 + 20 + 82 + 17 + + + + MSSQL + + + + + + 45 + 20 + 82 + 17 + + + + MySQL + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + false + + + + 0 + 0 + + + + + 300 + 300 + + + + Database browser + + + + + + + 0 + 0 + + + + + 130 + 0 + + + + + 130 + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Show + + + true + + + + + + + table: + + + + + + + + + + + + + + + + radio_mysql + radio_mssql + lineEdit_driver + lineEdit_server_address + spinBox_server_port + radio_sql_authentication + lineEdit_login + lineEdit_password + radio_windows_authentication + lineEdit_database_name + button_connect + comboBox_table_name + button_show_table + tableView_database_table + + + + + +