first commit

This commit is contained in:
2026-01-26 22:26:19 +03:00
commit 31fccd85f2
95 changed files with 115400 additions and 0 deletions
+928
View File
@@ -0,0 +1,928 @@
#include "udatabase.h"
#include <QMetaProperty>
#include <QSqlRecord>
#include <QDateTime>
#include <QTableWidget>
#include "qlistwidget.h"
uDataBase::uDataBase(const QString& dbFileName, QObject* parent)
: QObject(parent)
, m_dbFileName(dbFileName)
{
// Создаем уникальное имя подключения
QString connectionName = QString("settings_connection_%1").arg((quintptr)this);
// Открываем базу данных
m_db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
m_db.setDatabaseName(m_dbFileName);
if (!m_db.open()) {
m_lastError = m_db.lastError().text();
qWarning() << "Failed to open database:" << m_lastError;
return;
}
// Инициализируем базу данных
if (!initializeDatabase()) {
qWarning() << "Failed to initialize database";
}
}
uDataBase::~uDataBase()
{
if (m_db.isOpen()) {
QString connectionName = m_db.connectionName();
m_db.close();
m_db = QSqlDatabase(); // сбрасываем объект базы данных
QSqlDatabase::removeDatabase(connectionName); // удаляем соединение
}
}
bool uDataBase::close()
{
if (!isConnected()) {
return true; // Уже закрыто
}
// Сохраняем имя соединения перед закрытием
QString connectionName = m_db.connectionName();
// Закрываем базу данных
m_db.close();
// Очищаем объект базы данных
m_db = QSqlDatabase();
// Удаляем соединение из пула Qt
QSqlDatabase::removeDatabase(connectionName);
return true;
}
bool uDataBase::initializeDatabase()
{
// Создаем таблицу настроек, если она не существует
QString createTableQuery =
"CREATE TABLE IF NOT EXISTS params ("
"name TEXT PRIMARY KEY,"
"value TEXT"
")";
QSqlQuery query(m_db);
if (!query.exec(createTableQuery)) {
m_lastError = query.lastError().text();
qWarning() << "Failed to create table:" << m_lastError;
return false;
}
return true;
}
QString uDataBase::readSetting(const QString& aName, const QString& aDefault)
{
if (!m_db.isOpen()) {
qWarning() << "Database is not open";
return aDefault;
}
QSqlQuery query(m_db);
query.prepare("SELECT value FROM params WHERE name = :name");
query.bindValue(":name", aName);
if (!query.exec()) {
m_lastError = query.lastError().text();
qWarning() << "Failed to read setting" << aName << ":" << m_lastError;
return aDefault;
}
if (query.next()) {
return query.value(0).toString();
}
// Если настройка не найдена, возвращаем значение по умолчанию
return aDefault;
}
bool uDataBase::writeSetting(const QString& aName, const QString& aValue)
{
if (!m_db.isOpen()) {
qWarning() << "Database is not open";
return false;
}
// Используем INSERT OR REPLACE для обновления существующей записи или создания новой
QSqlQuery query(m_db);
query.prepare(
"INSERT OR REPLACE INTO params (name, value) "
"VALUES (:name, :value)"
);
query.bindValue(":name", aName);
query.bindValue(":value", aValue);
if (!query.exec()) {
m_lastError = query.lastError().text();
qWarning() << "Failed to write setting" << aName << ":" << m_lastError;
return false;
}
return true;
}
// В классе uDataBase добавьте эти методы:
/**
* @brief Специальная загрузка для таблицы таймеров с сохранением состояния
* @param tableWidget Таблица для загрузки
* @param timers Список таймеров для заполнения
* @return true если успешно
*/
bool uDataBase::LoadTimers(QTableWidget *tableWidget, QList<TimerInfo> &timers)
{
if (!tableWidget) {
m_lastError = "Table widget is null";
qWarning() << m_lastError;
return false;
}
// Получаем имя таблицы из objectName виджета
QString tableName = tableWidget->objectName();
if (tableName.isEmpty()) {
m_lastError = "Table widget has no object name";
qWarning() << m_lastError;
return false;
}
// Проверяем соединение с БД
if (!m_db.isOpen()) {
m_lastError = "Database is not open";
qWarning() << m_lastError;
return false;
}
// Очищаем таблицу и список таймеров
tableWidget->setRowCount(0);
timers.clear();
// Выполняем SQL запрос
QSqlQuery query(m_db);
QString sql = QString("SELECT * FROM %1 ORDER BY id").arg(tableName);
if (!query.exec(sql)) {
m_lastError = query.lastError().text();
qWarning() << "Query failed:" << m_lastError;
qWarning() << "SQL:" << sql;
return false;
}
int rowCount = 0;
while (query.next()) {
// Создаем объект таймера
TimerInfo timer;
timer.id = query.value("id").toInt();
timer.message = query.value("message").toString();
timer.interval = query.value("interval").toInt();
timer.isActive = query.value("is_active").toBool();
timer.isAnnouncement = query.value("is_announcement").toBool();
timer.timer = nullptr;
// Добавляем в список
timers.append(timer);
// Добавляем строку в таблицу
int row = tableWidget->rowCount();
tableWidget->insertRow(row);
// Чекбокс "Вкл"
QTableWidgetItem* enabledItem = new QTableWidgetItem();
enabledItem->setCheckState(timer.isActive ? Qt::Checked : Qt::Unchecked);
tableWidget->setItem(row, 0, enabledItem);
// Сообщение
QTableWidgetItem* messageItem = new QTableWidgetItem(timer.message);
tableWidget->setItem(row, 1, messageItem);
// Интервал
QTableWidgetItem* intervalItem = new QTableWidgetItem(QString::number(timer.interval));
tableWidget->setItem(row, 2, intervalItem);
// Чекбокс "О"
QTableWidgetItem* announcementItem = new QTableWidgetItem();
announcementItem->setCheckState(timer.isAnnouncement ? Qt::Checked : Qt::Unchecked);
tableWidget->setItem(row, 3, announcementItem);
rowCount++;
}
return true;
}
/**
* @brief Специальное сохранение для таблицы таймеров
* @param tableWidget Таблица для сохранения
* @param timers Список таймеров
* @return true если успешно
*/
bool uDataBase::SaveTimers(QTableWidget *tableWidget, const QList<TimerInfo> &timers)
{
if (!tableWidget) {
m_lastError = "Table widget is null";
qWarning() << m_lastError;
return false;
}
// Получаем имя таблицы из objectName виджета
QString tableName = tableWidget->objectName();
if (tableName.isEmpty()) {
m_lastError = "Table widget has no object name";
qWarning() << m_lastError;
return false;
}
// Проверяем соединение с БД
if (!m_db.isOpen()) {
m_lastError = "Database is not open";
qWarning() << m_lastError;
return false;
}
// Создаем таблицу если не существует
QString createTableQuery = QString(
"CREATE TABLE IF NOT EXISTS %1 ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"message TEXT NOT NULL,"
"interval INTEGER NOT NULL,"
"is_active BOOLEAN DEFAULT 1,"
"is_announcement BOOLEAN DEFAULT 0"
")"
).arg(tableName);
QSqlQuery createQuery(m_db);
if (!createQuery.exec(createTableQuery)) {
m_lastError = createQuery.lastError().text();
qWarning() << "Failed to create table:" << m_lastError;
return false;
}
// Начинаем транзакцию
if (!m_db.transaction()) {
m_lastError = m_db.lastError().text();
qWarning() << "Failed to start transaction:" << m_lastError;
return false;
}
// Очищаем таблицу
QSqlQuery clearQuery(m_db);
QString clearSql = QString("DELETE FROM %1").arg(tableName);
if (!clearQuery.exec(clearSql)) {
m_lastError = clearQuery.lastError().text();
qWarning() << "Failed to clear table:" << m_lastError;
m_db.rollback();
return false;
}
// Сохраняем таймеры
QSqlQuery insertQuery(m_db);
insertQuery.prepare(QString(
"INSERT INTO %1 (message, interval, is_active, is_announcement) "
"VALUES (:message, :interval, :is_active, :is_announcement)"
).arg(tableName));
int savedRows = 0;
for (const TimerInfo &timer : timers) {
insertQuery.bindValue(":message", timer.message);
insertQuery.bindValue(":interval", timer.interval);
insertQuery.bindValue(":is_active", timer.isActive);
insertQuery.bindValue(":is_announcement", timer.isAnnouncement);
if (!insertQuery.exec()) {
m_lastError = insertQuery.lastError().text();
qWarning() << "Failed to insert timer:" << m_lastError;
m_db.rollback();
return false;
}
savedRows++;
}
// Завершаем транзакцию
if (!m_db.commit()) {
m_lastError = m_db.lastError().text();
qWarning() << "Failed to commit transaction:" << m_lastError;
m_db.rollback();
return false;
}
qDebug() << "Saved" << savedRows << "timers to database";
return true;
}
bool uDataBase::LoadTableWidget(QTableWidget *tableWidget)
{
if (!tableWidget) {
m_lastError = "Table widget is null";
qWarning() << m_lastError;
return false;
}
// Получаем имя таблицы из objectName виджета
QString tableName = tableWidget->objectName();
if (tableName.isEmpty()) {
m_lastError = "Table widget has no object name";
qWarning() << m_lastError;
return false;
}
// Проверяем соединение с БД
if (!m_db.isOpen()) {
m_lastError = "Database is not open";
qWarning() << m_lastError;
return false;
}
// Очищаем таблицу (но сохраняем заголовки)
tableWidget->setRowCount(0);
// Выполняем SQL запрос
QSqlQuery query(m_db);
QString sql = QString("SELECT * FROM %1 ORDER BY id").arg(tableName);
if (!query.exec(sql)) {
m_lastError = query.lastError().text();
qWarning() << "Query failed:" << m_lastError;
qWarning() << "SQL:" << sql;
return false;
}
// Получаем информацию о колонках в результате запроса
QSqlRecord record = query.record();
int dbColumnCount = record.count();
// Определяем сколько колонок нам нужно в QTableWidget
int widgetColumnCount = tableWidget->columnCount();
int columnsToLoad = qMin(dbColumnCount - 1, widgetColumnCount); // -1 потому что пропускаем id
int rowCount = 0;
while (query.next()) {
// Добавляем новую строку
int row = tableWidget->rowCount();
tableWidget->insertRow(row);
// Заполняем ячейки (начиная с col0, пропускаем id)
for (int col = 0; col < columnsToLoad; col++) {
QString columnName = QString("col%1").arg(col);
QVariant value = query.value(columnName);
QTableWidgetItem *item = new QTableWidgetItem();
if (value.isNull()) {
item->setText("");
} else {
item->setText(value.toString());
}
// Устанавливаем выравнивание по умолчанию
item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
tableWidget->setItem(row, col, item);
}
rowCount++;
}
return true;
}
bool uDataBase::SaveTableWidget(QTableWidget *tableWidget)
{
if (!tableWidget) {
m_lastError = "Table widget is null";
qWarning() << m_lastError;
return false;
}
// Получаем имя таблицы из objectName виджета
QString tableName = tableWidget->objectName();
if (tableName.isEmpty()) {
m_lastError = "Table widget has no object name";
qWarning() << m_lastError;
return false;
}
// Проверяем соединение с БД
if (!m_db.isOpen()) {
m_lastError = "Database is not open";
qWarning() << m_lastError;
return false;
}
// Проверяем существует ли таблица
if (!tableExists(tableName)) {
// Создаем таблицу с нужным количеством колонок
if (!createTableForWidget(tableName, tableWidget->columnCount())) {
return false;
}
} else {
// Очищаем существующую таблицу
if (!clearTable(tableName)) {
return false;
}
}
// Сохраняем данные из таблицы
QSqlQuery query(m_db);
// Начинаем транзакцию для быстрой вставки
if (!m_db.transaction()) {
m_lastError = m_db.lastError().text();
qWarning() << "Failed to start transaction:" << m_lastError;
return false;
}
int rowCount = tableWidget->rowCount();
int colCount = tableWidget->columnCount();
if (colCount == 0) {
m_lastError = "Table widget has no columns";
qWarning() << m_lastError;
m_db.rollback();
return false;
}
// Подготавливаем SQL запрос для вставки
QStringList columnNames;
QStringList valuePlaceholders;
// Добавляем id (автоинкремент)
columnNames << "id";
valuePlaceholders << "NULL";
// Добавляем col0, col1, col2 и т.д.
for (int col = 0; col < colCount; col++) {
columnNames << QString("col%1").arg(col);
valuePlaceholders << QString(":col%1").arg(col);
}
QString sql = QString("INSERT INTO %1 (%2) VALUES (%3)")
.arg(tableName)
.arg(columnNames.join(", "))
.arg(valuePlaceholders.join(", "));
query.prepare(sql);
int savedRows = 0;
for (int row = 0; row < rowCount; row++) {
// Пропускаем пустые строки (если все ячейки пустые)
bool isEmptyRow = true;
for (int col = 0; col < colCount; col++) {
QTableWidgetItem *item = tableWidget->item(row, col);
if (item && !item->text().trimmed().isEmpty()) {
isEmptyRow = false;
break;
}
}
if (isEmptyRow) {
continue; // Пропускаем полностью пустые строки
}
// Биндим значения для каждой колонки
for (int col = 0; col < colCount; col++) {
QString paramName = QString(":col%1").arg(col);
QTableWidgetItem *item = tableWidget->item(row, col);
if (item && !item->text().isEmpty()) {
query.bindValue(paramName, item->text());
} else {
query.bindValue(paramName, QVariant(QVariant::String));
}
}
if (!query.exec()) {
m_lastError = query.lastError().text();
qWarning() << "Failed to insert row:" << m_lastError;
qWarning() << "SQL:" << sql;
m_db.rollback();
return false;
}
savedRows++;
}
// Завершаем транзакцию
if (!m_db.commit()) {
m_lastError = m_db.lastError().text();
qWarning() << "Failed to commit transaction:" << m_lastError;
m_db.rollback();
return false;
}
return true;
}
// Вспомогательные методы
bool uDataBase::tableExists(const QString &tableName)
{
QSqlQuery query(m_db);
QString sql = QString("SELECT name FROM sqlite_master WHERE type='table' AND name='%1'")
.arg(tableName);
if (query.exec(sql) && query.next()) {
return true;
}
return false;
}
bool uDataBase::createTableForWidget(const QString &tableName, int columnCount)
{
if (columnCount <= 0) {
m_lastError = "Invalid column count";
return false;
}
QSqlQuery query(m_db);
// Создаем SQL для создания таблицы
QStringList columns;
columns << "id INTEGER PRIMARY KEY AUTOINCREMENT";
for (int i = 0; i < columnCount; i++) {
columns << QString("col%1 TEXT").arg(i);
}
QString sql = QString("CREATE TABLE %1 (%2)").arg(tableName).arg(columns.join(", "));
if (!query.exec(sql)) {
m_lastError = query.lastError().text();
qWarning() << "Failed to create table:" << m_lastError;
qWarning() << "SQL:" << sql;
return false;
}
return true;
}
bool uDataBase::clearTable(const QString &tableName)
{
QSqlQuery query(m_db);
QString sql = QString("DELETE FROM %1").arg(tableName);
if (!query.exec(sql)) {
m_lastError = query.lastError().text();
qWarning() << "Failed to clear table:" << m_lastError;
return false;
}
// Сбрасываем автоинкремент
sql = QString("DELETE FROM sqlite_sequence WHERE name='%1'").arg(tableName);
query.exec(sql); // Игнорируем ошибки, т.к. может не быть sqlite_sequence
return true;
}
bool uDataBase::LoadRandomGroups(QListWidget *listWidget)
{
if (!listWidget) {
m_lastError = "List widget is null";
qWarning() << m_lastError;
return false;
}
listWidget->clear();
m_lastError.clear();
// Проверяем соединение с БД
if (!m_db.isOpen()) {
m_lastError = "Database is not open";
qWarning() << m_lastError;
return false;
}
// Проверяем существование таблицы
if (!tableExists("GroupResponse")) {
qWarning() << "Table GroupResponse doesn't exist";
// Можно создать таблицу автоматически
if (!createGroupResponseTable()) {
return false;
}
}
// Выполняем SQL запрос для получения уникальных имен
QSqlQuery query(m_db);
QString sql = "SELECT DISTINCT Name FROM GroupResponse ORDER BY Name";
if (!query.exec(sql)) {
m_lastError = query.lastError().text();
qWarning() << "Query failed:" << m_lastError;
qWarning() << "SQL:" << sql;
return false;
}
int count = 0;
while (query.next()) {
QString groupName = query.value(0).toString();
if (!groupName.isEmpty()) {
listWidget->addItem(groupName);
count++;
}
}
return true;
}
// Метод для загрузки ответов конкретной группы
bool uDataBase::LoadRandomResponses(const QString &groupName, QListWidget *listWidget)
{
if (!listWidget) {
m_lastError = "List widget is null";
qWarning() << m_lastError;
return false;
}
if (groupName.isEmpty()) {
m_lastError = "Group name is empty";
qWarning() << m_lastError;
return false;
}
listWidget->clear();
m_lastError.clear();
// Проверяем соединение с БД
if (!m_db.isOpen()) {
m_lastError = "Database is not open";
qWarning() << m_lastError;
return false;
}
// Проверяем существование таблицы
if (!tableExists("GroupResponse")) {
m_lastError = "Table GroupResponse doesn't exist";
qWarning() << m_lastError;
return false;
}
// Выполняем SQL запрос для получения ответов группы
QSqlQuery query(m_db);
query.prepare("SELECT Response FROM GroupResponse WHERE Name = :name ORDER BY id");
query.bindValue(":name", groupName);
if (!query.exec()) {
m_lastError = query.lastError().text();
qWarning() << "Query failed:" << m_lastError;
return false;
}
int count = 0;
while (query.next()) {
QString response = query.value(0).toString();
listWidget->addItem(response);
count++;
}
return true;
}
// Метод для получения случайного ответа из группы
QString uDataBase::GetRandomResponse(const QString &groupName)
{
if (groupName.isEmpty()) {
qWarning() << "Group name is empty";
return QString();
}
if (!m_db.isOpen()) {
qWarning() << "Database is not open";
return QString();
}
if (!tableExists("GroupResponse")) {
qWarning() << "Table GroupResponse doesn't exist";
return QString();
}
QSqlQuery query(m_db);
query.prepare("SELECT Response FROM GroupResponse WHERE Name = :name ORDER BY RANDOM() LIMIT 1");
query.bindValue(":name", groupName);
if (!query.exec()) {
qWarning() << "Query failed for group:" << groupName << "Error:" << query.lastError().text();
return QString();
}
if (!query.next()) {
qWarning() << "No responses found for group:" << groupName;
return QString();
}
return query.value(0).toString();
}
// Метод для обработки текста с заменой {{grN}}
QString uDataBase::ProcessResponseTemplate(const QString &templateText)
{
QRegularExpression re("\\{\\{(.+?)\\}\\}");
QRegularExpressionMatchIterator i = re.globalMatch(templateText);
QString result = templateText;
while (i.hasNext()) {
QRegularExpressionMatch match = i.next();
QString fullMatch = match.captured(0); // {{ANY_GROUP_NAME}}
QString groupName = match.captured(1).trimmed(); // ANY_GROUP_NAME
if (groupName.isEmpty()) {
continue; // Пропускаем пустые имена групп
}
QString replacement = GetRandomResponse(groupName);
if (replacement.isEmpty()) {
qWarning() << "No response found for group:" << groupName;
// Можно оставить оригинальный текст или заменить на что-то другое
replacement = QString("[Группа '%1' не найдена]").arg(groupName);
}
result.replace(fullMatch, replacement);
}
return result;
}
// Методы для добавления/удаления групп и ответов
bool uDataBase::AddGroupResponse(const QString &groupName, const QString &response)
{
if (groupName.isEmpty() || response.isEmpty()) {
m_lastError = "Group name or response is empty";
return false;
}
if (!m_db.isOpen()) {
m_lastError = "Database is not open";
return false;
}
// Создаем таблицу если она не существует
if (!tableExists("GroupResponse")) {
if (!createGroupResponseTable()) {
return false;
}
}
QSqlQuery query(m_db);
query.prepare("INSERT INTO GroupResponse (Name, Response) VALUES (:name, :response)");
query.bindValue(":name", groupName);
query.bindValue(":response", response);
if (!query.exec()) {
m_lastError = query.lastError().text();
qWarning() << "Failed to add response:" << m_lastError;
return false;
}
return true;
}
bool uDataBase::DeleteGroup(const QString &groupName)
{
if (groupName.isEmpty()) {
m_lastError = "Group name is empty";
return false;
}
if (!m_db.isOpen()) {
m_lastError = "Database is not open";
return false;
}
QSqlQuery query(m_db);
query.prepare("DELETE FROM GroupResponse WHERE Name = :name");
query.bindValue(":name", groupName);
if (!query.exec()) {
m_lastError = query.lastError().text();
qWarning() << "Failed to delete group:" << m_lastError;
return false;
}
return true;
}
bool uDataBase::DeleteResponse(const QString &groupName, const QString &ResponseName)
{
if (groupName.isEmpty()) {
m_lastError = "Group name is empty";
return false;
}
if (!m_db.isOpen()) {
m_lastError = "Database is not open";
return false;
}
// Получаем ID ответа по индексу в группе
QSqlQuery query(m_db);
// Удаляем ответ по ID
query.prepare("DELETE FROM GroupResponse WHERE Name = :name and Response = :response");
query.bindValue(":name", groupName);
query.bindValue(":response", ResponseName);
if (!query.exec()) {
m_lastError = query.lastError().text();
qWarning() << "Failed to delete response:" << m_lastError;
return false;
}
return true;
}
// Вспомогательные методы
bool uDataBase::createGroupResponseTable()
{
QSqlQuery query(m_db);
QString sql =
"CREATE TABLE IF NOT EXISTS GroupResponse ("
" id INTEGER PRIMARY KEY AUTOINCREMENT,"
" Name TEXT NOT NULL,"
" Response TEXT NOT NULL"
")";
if (!query.exec(sql)) {
m_lastError = query.lastError().text();
qWarning() << "Failed to create GroupResponse table:" << m_lastError;
return false;
}
// Создаем индекс для быстрого поиска по имени группы
sql = "CREATE INDEX IF NOT EXISTS idx_groupresponse_name ON GroupResponse (Name)";
query.exec(sql); // Игнорируем ошибки, если индекс уже существует
return true;
}
// Метод для сохранения списка ответов группы (заменяет все существующие)
bool uDataBase::SaveGroupResponses(const QString &groupName, const QStringList &responses)
{
if (groupName.isEmpty()) {
m_lastError = "Group name is empty";
return false;
}
if (!m_db.isOpen()) {
m_lastError = "Database is not open";
return false;
}
// Начинаем транзакцию
if (!m_db.transaction()) {
m_lastError = m_db.lastError().text();
return false;
}
// Удаляем старые ответы группы
QSqlQuery query(m_db);
query.prepare("DELETE FROM GroupResponse WHERE Name = :name");
query.bindValue(":name", groupName);
if (!query.exec()) {
m_lastError = query.lastError().text();
m_db.rollback();
return false;
}
// Добавляем новые ответы
query.prepare("INSERT INTO GroupResponse (Name, Response) VALUES (:name, :response)");
foreach (const QString &response, responses) {
if (!response.trimmed().isEmpty()) {
query.bindValue(":name", groupName);
query.bindValue(":response", response);
if (!query.exec()) {
m_lastError = query.lastError().text();
m_db.rollback();
return false;
}
}
}
if (!m_db.commit()) {
m_lastError = m_db.lastError().text();
m_db.rollback();
return false;
}
return true;
}
bool uDataBase::isConnected() const
{
return m_db.isOpen();
}
QString uDataBase::lastError() const
{
return m_lastError;
}