Compare commits
No commits in common. "91809469164fb9330e2fabb1d25ec4fb287b225f" and "ba6faaaf7e4a8d3bbc0b6d159d671c553cc2f4d0" have entirely different histories.
9180946916
...
ba6faaaf7e
|
|
@ -16,7 +16,6 @@ set(PROJECT_SOURCES
|
||||||
main.cpp
|
main.cpp
|
||||||
widget.cpp
|
widget.cpp
|
||||||
widget.h
|
widget.h
|
||||||
auth.h auth.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
|
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
|
||||||
|
|
@ -44,7 +43,7 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(mysitenews PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
|
target_link_libraries(mysitenews PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
|
||||||
Qt${QT_VERSION_MAJOR}::Sql
|
Qt6::Sql
|
||||||
)
|
)
|
||||||
|
|
||||||
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||||
|
|
|
||||||
107
README.md
107
README.md
|
|
@ -1,109 +1,2 @@
|
||||||
# mysitenews
|
# mysitenews
|
||||||
|
|
||||||
Графическое приложение на Qt для заполнения таблицы `random_data` в PostgreSQL.
|
|
||||||
|
|
||||||
При запуске открывается окно подключения к БД. После успешного подключения
|
|
||||||
появляется форма добавления записи с полями `name` и `text`, а также счётчик
|
|
||||||
текущего количества записей в таблице.
|
|
||||||
|
|
||||||
## Возможности
|
|
||||||
|
|
||||||
- подключение к PostgreSQL (host, port, user, password);
|
|
||||||
- сохранение последней успешной конфигурации подключения;
|
|
||||||
- отображение количества записей в `public.random_data`;
|
|
||||||
- добавление записей (`name`, `text`);
|
|
||||||
- показ ошибки при неудачном подключении.
|
|
||||||
|
|
||||||
## Требования
|
|
||||||
|
|
||||||
- CMake 3.16+;
|
|
||||||
- компилятор с поддержкой C++17;
|
|
||||||
- Qt 5 или Qt 6 (модули `Widgets` и `Sql`);
|
|
||||||
- драйвер PostgreSQL для Qt (`QPSQL`);
|
|
||||||
- сервер PostgreSQL.
|
|
||||||
|
|
||||||
Проверить наличие драйвера можно так:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ls /usr/lib/x86_64-linux-gnu/qt6/plugins/sqldrivers/
|
|
||||||
```
|
|
||||||
|
|
||||||
В каталоге должен быть `libqsqlpsql.so` (для Qt 5 — `libqsqlpsql.so` в
|
|
||||||
соответствующем каталоге плагинов). В Debian/Ubuntu драйвер ставится пакетом
|
|
||||||
`libqt6sql6-psql` (для Qt 5 — `libqt5sql5-psql`).
|
|
||||||
|
|
||||||
## Сборка и запуск
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
|
||||||
cmake --build build
|
|
||||||
./build/mysitenews
|
|
||||||
```
|
|
||||||
|
|
||||||
При сборке из Qt Creator используется каталог `build/Desktop-Debug`.
|
|
||||||
|
|
||||||
## База данных
|
|
||||||
|
|
||||||
Приложение обращается к БД `cards` и таблице `public.random_data`.
|
|
||||||
Ожидаемая структура таблицы:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE TABLE public.random_data (
|
|
||||||
id serial PRIMARY KEY,
|
|
||||||
name varchar(100) NOT NULL,
|
|
||||||
text text NOT NULL,
|
|
||||||
description text,
|
|
||||||
created_at timestamp DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Права пользователя
|
|
||||||
|
|
||||||
Пользователю БД нужны права на таблицу и на последовательность `id`:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
GRANT SELECT, INSERT ON public.random_data TO cards_user;
|
|
||||||
GRANT USAGE, SELECT ON SEQUENCE public.random_data_id_seq TO cards_user;
|
|
||||||
```
|
|
||||||
|
|
||||||
Без прав на последовательность вставка завершится ошибкой
|
|
||||||
`permission denied for sequence random_data_id_seq`.
|
|
||||||
|
|
||||||
## Использование
|
|
||||||
|
|
||||||
1. Запустите приложение.
|
|
||||||
2. Введите адрес сервера, порт (по умолчанию `5432`), имя пользователя и пароль.
|
|
||||||
3. Нажмите **OK**. При ошибке подключения будет показано сообщение.
|
|
||||||
4. Заполните поля `name` и `text` и нажмите **ADD**.
|
|
||||||
|
|
||||||
Поле `text` сохраняется как HTML (`QTextEdit::toHtml()`). Оба поля обязательны.
|
|
||||||
Длина `name` ограничена таблицей — 100 символов.
|
|
||||||
|
|
||||||
## Конфигурация
|
|
||||||
|
|
||||||
После успешного подключения параметры сохраняются через `QSettings`
|
|
||||||
(`QSettings("mysitenews", "mysitenews")`). В Linux файл находится по пути:
|
|
||||||
|
|
||||||
```
|
|
||||||
~/.config/mysitenews/mysitenews.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
Сохраняются `db/host`, `db/port`, `db/database`, `db/user`, `db/password`.
|
|
||||||
При следующем запуске поля формы заполняются этими значениями.
|
|
||||||
|
|
||||||
**Внимание:** пароль хранится в открытом виде. Не используйте этот способ
|
|
||||||
для продакшн-окружений; предпочтительнее `~/.pgpass` или переменные окружения.
|
|
||||||
|
|
||||||
## Структура проекта
|
|
||||||
|
|
||||||
| Файл | Назначение |
|
|
||||||
| --- | --- |
|
|
||||||
| `main.cpp` | точка входа, связывает окно авторизации и рабочее окно |
|
|
||||||
| `auth.h` / `auth.cpp` | окно подключения к БД, сохранение конфигурации |
|
|
||||||
| `widget.h` / `widget.cpp` | форма добавления записей и подсчёт количества |
|
|
||||||
| `CMakeLists.txt` | конфигурация сборки |
|
|
||||||
|
|
||||||
## Лицензия
|
|
||||||
|
|
||||||
Проект распространяется под GNU General Public License v3. Подробности — в
|
|
||||||
файле [LICENSE](LICENSE).
|
|
||||||
|
|
|
||||||
57
auth.cpp
57
auth.cpp
|
|
@ -1,35 +1,20 @@
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <QSettings>
|
|
||||||
#include <QSqlError>
|
|
||||||
|
|
||||||
auth::auth (QWidget *parent)
|
auth::auth (QWidget *parent)
|
||||||
: QWidget{parent}
|
: QWidget{ parent }, _nicknameLabel (new QLabel ("nickname:")),
|
||||||
, _nicknameLabel(new QLabel("nickname:"))
|
_nicknameEdit (new QLineEdit ()),
|
||||||
, _nicknameEdit(new QLineEdit())
|
_passwordLabel (new QLabel ("passwprd:")),
|
||||||
, _passwordLabel(new QLabel("passwprd:"))
|
_passwordEdit (new QLineEdit ()),
|
||||||
, _passwordEdit(new QLineEdit())
|
_addressLabel (new QLabel ("address:")), _addressEdit (new QLineEdit ()),
|
||||||
, _addressLabel(new QLabel("address:"))
|
_portLabel (new QLabel ("port:")), _portEdit (new QLineEdit ()),
|
||||||
, _addressEdit(new QLineEdit())
|
_okButton (new QPushButton ("&OK")),
|
||||||
, _portLabel(new QLabel("port:"))
|
_quitButton (new QPushButton ("&QUIT")), _vbox (new QVBoxLayout (parent))
|
||||||
, _portEdit(new QLineEdit())
|
|
||||||
, _okButton(new QPushButton("&OK"))
|
|
||||||
, _quitButton(new QPushButton("&QUIT"))
|
|
||||||
, _vbox(new QVBoxLayout(this))
|
|
||||||
, _db(QSqlDatabase::addDatabase("QPSQL"))
|
|
||||||
{
|
{
|
||||||
_nicknameLabel->setAlignment (Qt::AlignHCenter);
|
_nicknameLabel->setAlignment (Qt::AlignHCenter);
|
||||||
_passwordLabel->setAlignment (Qt::AlignHCenter);
|
_passwordLabel->setAlignment (Qt::AlignHCenter);
|
||||||
_addressLabel->setAlignment (Qt::AlignHCenter);
|
_addressLabel->setAlignment (Qt::AlignHCenter);
|
||||||
_portLabel->setAlignment (Qt::AlignHCenter);
|
_portLabel->setAlignment (Qt::AlignHCenter);
|
||||||
_passwordEdit->setEchoMode (QLineEdit::Password);
|
_passwordEdit->setEchoMode (QLineEdit::Password);
|
||||||
|
|
||||||
QSettings settings ("mysitenews", "mysitenews");
|
|
||||||
_nicknameEdit->setText (settings.value ("db/user").toString ());
|
|
||||||
_passwordEdit->setText (settings.value ("db/password").toString ());
|
|
||||||
_addressEdit->setText (settings.value ("db/host").toString ());
|
|
||||||
_portEdit->setText (settings.value ("db/port", "5432").toString ());
|
|
||||||
_vbox->addWidget (_nicknameLabel);
|
_vbox->addWidget (_nicknameLabel);
|
||||||
_vbox->addWidget (_nicknameEdit);
|
_vbox->addWidget (_nicknameEdit);
|
||||||
_vbox->addWidget (_passwordLabel);
|
_vbox->addWidget (_passwordLabel);
|
||||||
|
|
@ -48,24 +33,16 @@ auth::auth(QWidget *parent)
|
||||||
void
|
void
|
||||||
auth::onAuth ()
|
auth::onAuth ()
|
||||||
{
|
{
|
||||||
QSettings settings ("mysitenews", "mysitenews");
|
QSqlDatabase *db = new QSqlDatabase(QSqlDatabase::addDatabase("QPSQL"));
|
||||||
_db.setHostName(_addressEdit->text());
|
db->setHostName(_addressEdit->text());
|
||||||
_db.setPort(_portEdit->text().toInt());
|
db->setPort(_portEdit->text().toInt());
|
||||||
_db.setDatabaseName(settings.value ("db/database", "cards").toString ());
|
db->setDatabaseName("cards");
|
||||||
_db.setUserName(_nicknameEdit->text());
|
db->setUserName(_nicknameEdit->text());
|
||||||
_db.setPassword(_passwordEdit->text());
|
db->setPassword(_passwordEdit->text());
|
||||||
if (!_db.open()) {
|
bool ok = db->open();
|
||||||
QMessageBox::critical(this, tr("Ошибка подключения"),
|
if (ok) {
|
||||||
_db.lastError().text());
|
emit dbOpenned(db);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.setValue ("db/host", _db.hostName ());
|
|
||||||
settings.setValue ("db/port", _db.port ());
|
|
||||||
settings.setValue ("db/database", _db.databaseName ());
|
|
||||||
settings.setValue ("db/user", _db.userName ());
|
|
||||||
settings.setValue ("db/password", _db.password ());
|
|
||||||
|
|
||||||
emit dbOpenned(_db);
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
auth.h
3
auth.h
|
|
@ -26,11 +26,10 @@ private:
|
||||||
QPushButton *_okButton = nullptr;
|
QPushButton *_okButton = nullptr;
|
||||||
QPushButton *_quitButton = nullptr;
|
QPushButton *_quitButton = nullptr;
|
||||||
QVBoxLayout *_vbox = nullptr;
|
QVBoxLayout *_vbox = nullptr;
|
||||||
QSqlDatabase _db;
|
|
||||||
public slots:
|
public slots:
|
||||||
void onAuth ();
|
void onAuth ();
|
||||||
signals:
|
signals:
|
||||||
void dbOpenned(const QSqlDatabase &);
|
void dbOpenned(QSqlDatabase *);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AUTH_H
|
#endif // AUTH_H
|
||||||
|
|
|
||||||
65
widget.cpp
65
widget.cpp
|
|
@ -1,68 +1,5 @@
|
||||||
#include "widget.h"
|
#include "widget.h"
|
||||||
#include <QSqlQuery>
|
|
||||||
|
|
||||||
Widget::Widget(QWidget *parent)
|
Widget::Widget (QWidget *parent) : QWidget (parent) {}
|
||||||
: QWidget(parent)
|
|
||||||
, _grid(new QGridLayout(this))
|
|
||||||
, _countId(new QLabel("NULL"))
|
|
||||||
, _nameLabel(new QLabel("name: "))
|
|
||||||
, _nameEdit(new QLineEdit())
|
|
||||||
, _textLabel(new QLabel("text:"))
|
|
||||||
, _textEdit(new QTextEdit())
|
|
||||||
, _addBtn(new QPushButton("&ADD"))
|
|
||||||
{
|
|
||||||
_grid->addWidget(_countId, 0, 0, Qt::AlignHCenter);
|
|
||||||
_grid->addWidget(_nameLabel, 1, 0, Qt::AlignRight);
|
|
||||||
_grid->addWidget(_nameEdit, 1, 1, Qt::AlignVCenter);
|
|
||||||
_grid->addWidget(_textLabel, 2, 0, Qt::AlignRight);
|
|
||||||
_grid->addWidget(_textEdit, 2, 1, Qt::AlignHCenter);
|
|
||||||
_grid->addWidget(_addBtn, 3, 0, Qt::AlignHCenter);
|
|
||||||
setLayout(_grid);
|
|
||||||
|
|
||||||
connect(_addBtn, &QPushButton::clicked, this, &Widget::addRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget::~Widget () {}
|
Widget::~Widget () {}
|
||||||
|
|
||||||
void Widget::init(const QSqlDatabase &db)
|
|
||||||
{
|
|
||||||
_db = db;
|
|
||||||
if (!_db.isOpen()) {
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
updateCount();
|
|
||||||
show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Widget::updateCount()
|
|
||||||
{
|
|
||||||
if (!_db.isOpen()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QSqlQuery query(_db);
|
|
||||||
if (query.exec("SELECT COUNT(*) FROM random_data") && query.next()) {
|
|
||||||
_countId->setText(query.value(0).toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Widget::addRecord()
|
|
||||||
{
|
|
||||||
if (_nameEdit->displayText().isEmpty() || _textEdit->toPlainText().isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!_db.isOpen()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QSqlQuery query(_db);
|
|
||||||
query.prepare("INSERT INTO public.random_data (name, text) "
|
|
||||||
"VALUES (?, ?)");
|
|
||||||
query.addBindValue(_nameEdit->displayText());
|
|
||||||
query.addBindValue(_textEdit->toHtml());
|
|
||||||
if (!query.exec()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_nameEdit->clear();
|
|
||||||
_textEdit->clear();
|
|
||||||
updateCount();
|
|
||||||
}
|
|
||||||
|
|
|
||||||
20
widget.h
20
widget.h
|
|
@ -1,12 +1,6 @@
|
||||||
#ifndef WIDGET_H
|
#ifndef WIDGET_H
|
||||||
#define WIDGET_H
|
#define WIDGET_H
|
||||||
|
|
||||||
#include <QGridLayout>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QLineEdit>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QSqlDatabase>
|
|
||||||
#include <QTextEdit>
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
class Widget : public QWidget
|
class Widget : public QWidget
|
||||||
|
|
@ -16,19 +10,5 @@ class Widget : public QWidget
|
||||||
public:
|
public:
|
||||||
Widget (QWidget *parent = nullptr);
|
Widget (QWidget *parent = nullptr);
|
||||||
~Widget ();
|
~Widget ();
|
||||||
|
|
||||||
private:
|
|
||||||
QSqlDatabase _db;
|
|
||||||
QGridLayout *_grid = nullptr;
|
|
||||||
QLabel *_countId = nullptr;
|
|
||||||
QLabel *_nameLabel = nullptr;
|
|
||||||
QLineEdit *_nameEdit = nullptr;
|
|
||||||
QLabel *_textLabel = nullptr;
|
|
||||||
QTextEdit *_textEdit = nullptr;
|
|
||||||
QPushButton *_addBtn = nullptr;
|
|
||||||
void updateCount();
|
|
||||||
public slots:
|
|
||||||
void init(const QSqlDatabase &);
|
|
||||||
void addRecord();
|
|
||||||
};
|
};
|
||||||
#endif // WIDGET_H
|
#endif // WIDGET_H
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue