Skip to content

Commit 81d367e

Browse files
committed
feat: implement connection to database
1 parent 2e587e8 commit 81d367e

File tree

8 files changed

+443
-22
lines changed

8 files changed

+443
-22
lines changed

src/CMakeLists.txt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
set(SOURCE_FILES main.cpp
22
QBrowserApp.cpp
33
QConnectionDialog.cpp
4-
QSqlCtrl.cpp
5-
QSqlQueryPanel.cpp)
4+
QSqlQueryPanel.cpp
5+
QConnectionCtrl.cpp
6+
QDbTableView.cpp)
67

78
set(HEADERS QBrowserApp.h
89
QConnectionDialog.h
9-
QSqlCtrl.h
10-
QSqlQueryPanel.h)
11-
10+
QSqlQueryPanel.h
11+
QConnectionCtrl.h
12+
QDbTableView.h)
13+
1214
if(WIN32)
1315
set(RESOURCES res/app_icon.rc)
1416
source_group("Resource files" FILES ${RESOURCES})

src/QConnectionCtrl.cpp

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
#include "QConnectionCtrl.h"
2+
3+
QConnectionCtrl::QConnectionCtrl(QWidget* parent) {
4+
QTreeWidgetItem* rootItem = new QTreeWidgetItem();
5+
rootItem->setIcon(0, QApplication::style()->standardIcon(QStyle::SP_ComputerIcon));
6+
rootItem->setText(0, tr("Databases"));
7+
setHeaderItem(rootItem);
8+
9+
setMinimumSize(250, 252);
10+
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
11+
setFrameStyle(QFrame::Panel | QFrame::Sunken);
12+
setLineWidth(2);
13+
14+
QAction* refreshAction = new QAction(tr("Refresh"), this);
15+
QAction* shemaAction = new QAction(tr("Show shema"), this);
16+
shemaAction->setEnabled(false);
17+
shemaAction->setObjectName("shemaAction");
18+
QAction* separator = new QAction;
19+
separator->setSeparator(true);
20+
QAction* addConnectionAction = new QAction(tr("Add Connection..."), this);
21+
22+
connect(refreshAction, &QAction::triggered, this, [=]() { refresh(); });
23+
connect(shemaAction, &QAction::triggered, this, &QConnectionCtrl::showMetaData);
24+
connect(addConnectionAction, &QAction::triggered, this, &QConnectionCtrl::showConnectionDialog);
25+
26+
addAction(refreshAction);
27+
addAction(shemaAction);
28+
addAction(separator);
29+
addAction(addConnectionAction);
30+
setContextMenuPolicy(Qt::ActionsContextMenu);
31+
32+
connect(this, &QTreeWidget::itemActivated, [=](QTreeWidgetItem* item, int column) {setActiveItem(item, column); });
33+
connect(this, &QTreeWidget::currentItemChanged, [=](QTreeWidgetItem* current, QTreeWidgetItem* previous) {
34+
findChild<QAction*>("shemaAction")->setEnabled(current && current->parent());
35+
});
36+
37+
setObjectName("connectionCtrl");
38+
39+
emit statusMessage(tr("Ready."));
40+
}
41+
42+
QSqlError QConnectionCtrl::createConnection(const QString& driver, const QString& dbName, const QString& host, const QString& user, const QString& passwd, const int port) {
43+
static int cCount = 0;
44+
QSqlDatabase db = QSqlDatabase::addDatabase(driver, QString("dbConnection%1").arg(++cCount));
45+
db.setDatabaseName(dbName);
46+
db.setHostName(host);
47+
db.setPort(port);
48+
49+
QSqlError err;
50+
if (!db.open(user, passwd)) {
51+
err = db.lastError();
52+
db = QSqlDatabase();
53+
QSqlDatabase::removeDatabase(QString("dbConnection%1").arg(cCount));
54+
}
55+
56+
return err;
57+
}
58+
59+
void QConnectionCtrl::addConnection(const QString& driver, const QString& dbName, const QString& host, const QString& user, const QString& passwd, const int port) {
60+
QSqlError err = createConnection(driver, dbName, host, user, passwd, port);
61+
if (err.type() != QSqlError::NoError)
62+
QMessageBox::warning(this, tr("Unable to open database"), tr("An error occurred while "
63+
"opening the connection: ") + err.text());
64+
else refresh();
65+
}
66+
67+
void QConnectionCtrl::showConnectionDialog() {
68+
QConnectionDialog dialog(this);
69+
connect(&dialog, &QConnectionDialog::connectionAdded, this, &QConnectionCtrl::addConnection);
70+
dialog.exec();
71+
}
72+
73+
void QConnectionCtrl::showMetaData() {
74+
QTreeWidgetItem* cItem = currentItem();
75+
if (!cItem || !cItem->parent())
76+
return;
77+
setActiveDb(cItem->parent());
78+
79+
QString t = cItem->text(0);
80+
81+
QSqlRecord rec = QSqlDatabase::database(m_activeDb).record(t);
82+
QStandardItemModel* model = new QStandardItemModel(nullptr);
83+
84+
model->insertRows(0, rec.count());
85+
model->insertColumns(0, 7);
86+
87+
model->setHeaderData(0, Qt::Horizontal, "Fieldname");
88+
model->setHeaderData(1, Qt::Horizontal, "Type");
89+
model->setHeaderData(2, Qt::Horizontal, "Length");
90+
model->setHeaderData(3, Qt::Horizontal, "Precision");
91+
model->setHeaderData(4, Qt::Horizontal, "Required");
92+
model->setHeaderData(5, Qt::Horizontal, "AutoValue");
93+
model->setHeaderData(6, Qt::Horizontal, "DefaultValue");
94+
95+
for (int i = 0; i < rec.count(); ++i) {
96+
QSqlField fld = rec.field(i);
97+
model->setData(model->index(i, 0), fld.name());
98+
model->setData(model->index(i, 1), fld.typeID() == -1
99+
? QString(fld.metaType().name())
100+
: QString("%1 (%2)").arg(fld.metaType().name()).arg(fld.typeID()));
101+
model->setData(model->index(i, 2), fld.length());
102+
model->setData(model->index(i, 3), fld.precision());
103+
model->setData(model->index(i, 4), fld.requiredStatus() == -1 ? QVariant("?")
104+
: QVariant(bool(fld.requiredStatus())));
105+
model->setData(model->index(i, 5), fld.isAutoValue());
106+
model->setData(model->index(i, 6), fld.defaultValue());
107+
}
108+
109+
emit tableModelChanged(model, QAbstractItemView::NoEditTriggers);
110+
}
111+
112+
void QConnectionCtrl::setActiveItem(QTreeWidgetItem* item, int column) {
113+
if (!item)
114+
return;
115+
116+
if (item->parent()) {
117+
setActiveDb(item->parent());
118+
119+
const QString t = item->text(0);
120+
QSqlDatabase db = QSqlDatabase::database(m_activeDb);
121+
122+
QSqlTableModel* model = new CustomModel(nullptr, db);
123+
model->setEditStrategy(QSqlTableModel::OnRowChange);
124+
model->setTable(db.driver()->escapeIdentifier(t, QSqlDriver::TableName));
125+
model->select();
126+
if (model->lastError().type() != QSqlError::NoError)
127+
emit statusMessage(model->lastError().text());
128+
129+
emit tableModelChanged(model, QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
130+
}
131+
else setActiveDb(item);
132+
}
133+
134+
void QConnectionCtrl::refresh() {
135+
clear();
136+
QStringList connectionNames = QSqlDatabase::connectionNames();
137+
138+
bool gotActiveDb = false;
139+
for (int i = 0; i < connectionNames.count(); ++i) {
140+
QTreeWidgetItem* rootItem = new QTreeWidgetItem(this);
141+
QSqlDatabase db = QSqlDatabase::database(connectionNames.at(i), false);
142+
143+
QString dbCaption = db.driverName();
144+
dbCaption.append(QLatin1Char(':'));
145+
if (!db.userName().isEmpty())
146+
dbCaption.append(db.userName()).append(QLatin1Char('@'));
147+
dbCaption.append(db.databaseName());
148+
149+
rootItem->setText(0, dbCaption);
150+
rootItem->setIcon(0, QApplication::style()->standardIcon(QStyle::SP_DriveNetIcon));
151+
if (connectionNames.at(i) == QSqlDatabase::database(m_activeDb).connectionName()) {
152+
gotActiveDb = true;
153+
setActiveDb(rootItem);
154+
}
155+
if (db.isOpen()) {
156+
QStringList tableList = db.tables();
157+
for (int t = 0; t < tableList.count(); ++t) {
158+
QTreeWidgetItem* table = new QTreeWidgetItem(rootItem);
159+
table->setText(0, tableList.at(t));
160+
table->setIcon(0, QApplication::style()->standardIcon(QStyle::SP_DirOpenIcon));
161+
}
162+
}
163+
}
164+
if (!gotActiveDb) {
165+
m_activeDb = connectionNames.value(0);
166+
setActiveDb(topLevelItem(0));
167+
}
168+
169+
doItemsLayout();
170+
}
171+
172+
void QConnectionCtrl::setActiveDb(QTreeWidgetItem* item) {
173+
auto f_setBold = [=](QTreeWidgetItem* item, bool bold) {
174+
QFont font = item->font(0);
175+
font.setBold(bold);
176+
item->setFont(0, font);
177+
};
178+
179+
for (int i = 0; i < topLevelItemCount(); ++i) {
180+
if (topLevelItem(i)->font(0).bold())
181+
f_setBold(topLevelItem(i), false);
182+
}
183+
184+
if (!item)
185+
return;
186+
187+
f_setBold(item, true);
188+
m_activeDb = QSqlDatabase::connectionNames().value(indexOfTopLevelItem(item));
189+
}

src/QConnectionCtrl.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#pragma once
2+
3+
#ifndef CONNECTIONCTRL_H
4+
#define CONNECTIONCTRL_H
5+
6+
#include <QObject>
7+
#include <QTreeWidget>
8+
#include <QSqlDatabase>
9+
#include <QSqlTableModel>
10+
#include <QSqlDriver>
11+
#include <QSqlRecord>
12+
#include <QSqlError>
13+
#include <QSqlField>
14+
#include <QStandardItemModel>
15+
#include <QCursor>
16+
#include <QBrush>
17+
18+
#include "QConnectionDialog.h"
19+
20+
class QConnectionCtrl : public QTreeWidget
21+
{
22+
Q_OBJECT;
23+
public:
24+
explicit QConnectionCtrl(QWidget* parent = nullptr);
25+
static QSqlError createConnection(const QString& driver, const QString& dbName, const QString& host, const QString& user, const QString& passwd, const int port = -1);
26+
public slots:
27+
void addConnection(const QString& driver, const QString& dbName, const QString& host, const QString& user, const QString& passwd, const int port = -1);
28+
void showConnectionDialog();
29+
void showMetaData();
30+
void setActiveItem(QTreeWidgetItem* item, int column);
31+
signals:
32+
void tableModelChanged(QAbstractItemModel* model, QAbstractItemView::EditTriggers triggers);
33+
void statusMessage(const QString& message);
34+
protected:
35+
QString m_activeDb;
36+
37+
void refresh();
38+
void setActiveDb(QTreeWidgetItem* item);
39+
};
40+
41+
class CustomModel : public QSqlTableModel
42+
{
43+
Q_OBJECT
44+
public:
45+
explicit CustomModel(QObject* parent = nullptr, QSqlDatabase db = QSqlDatabase())
46+
: QSqlTableModel(parent, db) {}
47+
48+
QVariant data(const QModelIndex& idx, int role) const override
49+
{
50+
if (role == Qt::BackgroundRole && isDirty(idx))
51+
return QBrush(QColor(255, 0, 0, 127));
52+
return QSqlTableModel::data(idx, role);
53+
}
54+
};
55+
56+
#endif

src/QConnectionDialog.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
#include "QConnectionDialog.h"
22

3-
QConnectionDialog::QConnectionDialog(QWidget* parent, Qt::WindowFlags f)
4-
: QDialog(parent, f) {
3+
QConnectionDialog::QConnectionDialog(QWidget* parent, Qt::WindowFlags f) {
54
setWindowTitle(QObject::tr("Connect..."));
65
setMinimumSize(330, 300);
76

87
QGroupBox* groupBox = new QGroupBox(QObject::tr("Connection settings"));
98
QGridLayout* groupBoxLayout = new QGridLayout;
109

11-
QComboBox* comboBox = new QComboBox;
10+
comboBox = new QComboBox;
1211
QStringList drivers = QSqlDatabase::drivers();
1312
if (!drivers.contains("QSQLITE")) comboBox->setEnabled(false);
1413
comboBox->addItems(drivers);
1514

16-
QSpinBox* spinBox = new QSpinBox;
15+
dbEdit = new QLineEdit;
16+
userEdit = new QLineEdit;
17+
passwordEdit = new QLineEdit;
18+
passwordEdit->setEchoMode(QLineEdit::Password);
19+
hostEdit = new QLineEdit;
20+
21+
spinBox = new QSpinBox;
1722
spinBox->setMaximum(65535);
1823
spinBox->setMinimum(-1);
1924
spinBox->setValue(-1);
@@ -23,21 +28,21 @@ QConnectionDialog::QConnectionDialog(QWidget* parent, Qt::WindowFlags f)
2328
groupBoxLayout->addWidget(new QLabel(tr("Driver:")), 0, 0);
2429
groupBoxLayout->addWidget(comboBox, 0, 1);
2530
groupBoxLayout->addWidget(new QLabel(tr("Database Name:")), 1, 0);
26-
groupBoxLayout->addWidget(new QLineEdit, 1, 1);
31+
groupBoxLayout->addWidget(dbEdit, 1, 1);
2732
groupBoxLayout->addWidget(new QLabel(tr("Username:")), 2, 0);
28-
groupBoxLayout->addWidget(new QLineEdit, 2, 1);
33+
groupBoxLayout->addWidget(userEdit, 2, 1);
2934
groupBoxLayout->addWidget(new QLabel(tr("Password:")), 3, 0);
30-
groupBoxLayout->addWidget(new QLineEdit, 3, 1);
35+
groupBoxLayout->addWidget(passwordEdit, 3, 1);
3136
groupBoxLayout->addWidget(new QLabel(tr("Hostname:")), 4, 0);
32-
groupBoxLayout->addWidget(new QLineEdit, 4, 1);
37+
groupBoxLayout->addWidget(hostEdit, 4, 1);
3338
groupBoxLayout->addWidget(new QLabel(tr("Port: ")), 5, 0);
3439
groupBoxLayout->addWidget(spinBox, 5, 1);
3540
groupBox->setLayout(groupBoxLayout);
3641

3742
QPushButton* acceptButton = new QPushButton(tr("OK"));
3843
QPushButton* rejectButton = new QPushButton(tr("Cancel"));
39-
connect(acceptButton, &QPushButton::clicked, this, [=]() {accept(); });
40-
connect(rejectButton, &QPushButton::clicked, this, [=]() {reject();; });
44+
connect(acceptButton, &QPushButton::clicked, this, &QConnectionDialog::on_accept);
45+
connect(rejectButton, &QPushButton::clicked, this, [=]() {reject(); });
4146

4247
QWidget* buttonpanel = new QWidget;
4348
QHBoxLayout* buttonHLayout = new QHBoxLayout;
@@ -53,4 +58,9 @@ QConnectionDialog::QConnectionDialog(QWidget* parent, Qt::WindowFlags f)
5358
#ifdef Q_OS_WINDOWS
5459
setWindowIcon(QApplication::style()->standardIcon(QStyle::SP_CommandLink));
5560
#endif
56-
}
61+
}
62+
63+
void QConnectionDialog::on_accept() {
64+
emit connectionAdded(comboBox->currentText(), dbEdit->text(), hostEdit->text(), userEdit->text(), passwordEdit->text(), spinBox->value());
65+
accept();
66+
}

src/QConnectionDialog.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,27 @@
1212
#include <QLineEdit>
1313
#include <QSpinBox>
1414
#include <QPushButton>
15-
#include <QSqlDatabase>
1615
#include <QApplication>
16+
#include <QMessageBox>
17+
#include <QSqlDatabase>
18+
#include <QSqlDriver>
1719

1820
class QConnectionDialog : public QDialog
1921
{
2022
Q_OBJECT
2123
public:
2224
QConnectionDialog(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
25+
signals:
26+
void connectionAdded(const QString& driver, const QString& dbName, const QString& host, const QString& user, const QString& passwd, const int port = -1);
27+
protected:
28+
QComboBox* comboBox;
29+
QLineEdit* dbEdit;
30+
QLineEdit* userEdit;
31+
QLineEdit* passwordEdit;
32+
QLineEdit* hostEdit;
33+
QSpinBox* spinBox;
34+
35+
void on_accept();
2336
};
2437

2538
#endif

0 commit comments

Comments
 (0)