добавил обработку счетчиков
This commit is contained in:
@@ -22,6 +22,7 @@ INCLUDEPATH += $$PWD
|
|||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
commandprocessor.cpp \
|
commandprocessor.cpp \
|
||||||
|
countermanager.cpp \
|
||||||
emoteprovider.cpp \
|
emoteprovider.cpp \
|
||||||
fcolorsetting.cpp \
|
fcolorsetting.cpp \
|
||||||
fcreatechat.cpp \
|
fcreatechat.cpp \
|
||||||
@@ -54,6 +55,7 @@ SOURCES += \
|
|||||||
HEADERS += \
|
HEADERS += \
|
||||||
badge.h \
|
badge.h \
|
||||||
commandprocessor.h \
|
commandprocessor.h \
|
||||||
|
countermanager.h \
|
||||||
emoteprovider.h \
|
emoteprovider.h \
|
||||||
fcolorsetting.h \
|
fcolorsetting.h \
|
||||||
fcreatechat.h \
|
fcreatechat.h \
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ QString CommandProcessor::processCommand(const QString &sender, const QString &f
|
|||||||
response = parseTextFiles(response);
|
response = parseTextFiles(response);
|
||||||
response = parseBan(response, sender);
|
response = parseBan(response, sender);
|
||||||
response = parseAPI(response, sender);
|
response = parseAPI(response, sender);
|
||||||
|
response = parseCounters(response);
|
||||||
|
|
||||||
if (response.contains("[AI]", Qt::CaseInsensitive)) {
|
if (response.contains("[AI]", Qt::CaseInsensitive)) {
|
||||||
response = parseAI(response, parameters);
|
response = parseAI(response, parameters);
|
||||||
@@ -214,6 +215,24 @@ QString CommandProcessor::parseTextFiles(const QString &response)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString CommandProcessor::parseCounters(const QString &response)
|
||||||
|
{
|
||||||
|
QString result = response;
|
||||||
|
// Используем ленивый квантификатор +?
|
||||||
|
QRegularExpression regex("\\|\\)([^\\)]+?)\\|\\)");
|
||||||
|
QRegularExpressionMatchIterator matches = regex.globalMatch(response);
|
||||||
|
|
||||||
|
while (matches.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = matches.next();
|
||||||
|
QString counterName = match.captured(1);
|
||||||
|
int count = m_context.counterManager->getCount(counterName);
|
||||||
|
QString countStr = QString::number(count);
|
||||||
|
result.replace("|)" + counterName + "|)", countStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QString CommandProcessor::parseRandomGroups(const QString &response)
|
QString CommandProcessor::parseRandomGroups(const QString &response)
|
||||||
{
|
{
|
||||||
QString result = response;
|
QString result = response;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QDate>
|
#include <QDate>
|
||||||
|
#include "countermanager.h"
|
||||||
#include "mediafilemanager.h"
|
#include "mediafilemanager.h"
|
||||||
#include "user_manager.h"
|
#include "user_manager.h"
|
||||||
#include "ttw_api.h"
|
#include "ttw_api.h"
|
||||||
@@ -34,6 +35,7 @@ public:
|
|||||||
RandomResponses* randomResponses = nullptr;
|
RandomResponses* randomResponses = nullptr;
|
||||||
MediaFileManager* mediaFileManager = nullptr;
|
MediaFileManager* mediaFileManager = nullptr;
|
||||||
NeuralTemplateManager *neuralTemplateManager = nullptr;
|
NeuralTemplateManager *neuralTemplateManager = nullptr;
|
||||||
|
CounterManager* counterManager = nullptr;
|
||||||
QString channel;
|
QString channel;
|
||||||
int notifyVolume = 50;
|
int notifyVolume = 50;
|
||||||
};
|
};
|
||||||
@@ -67,6 +69,7 @@ private:
|
|||||||
QString parseAPI(const QString &response, const QString &sender);
|
QString parseAPI(const QString &response, const QString &sender);
|
||||||
QString parseAI(const QString &response, const QString &question);
|
QString parseAI(const QString &response, const QString &question);
|
||||||
QString parseNeuralTemplates(const QString &response, const QString &sender, const QString ¶meters);
|
QString parseNeuralTemplates(const QString &response, const QString &sender, const QString ¶meters);
|
||||||
|
QString parseCounters(const QString &response);
|
||||||
|
|
||||||
QString extractParameters(const QString &fullCommand);
|
QString extractParameters(const QString &fullCommand);
|
||||||
QString getUsernameByIndex(QString userIndex) const;
|
QString getUsernameByIndex(QString userIndex) const;
|
||||||
|
|||||||
@@ -0,0 +1,215 @@
|
|||||||
|
// countermanager.cpp
|
||||||
|
#include "countermanager.h"
|
||||||
|
#include <QTableWidget>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
CounterManager::CounterManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_database(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CounterManager::~CounterManager()
|
||||||
|
{
|
||||||
|
if (m_database) {
|
||||||
|
saveToDatabase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CounterManager::initialize(uDataBase *database)
|
||||||
|
{
|
||||||
|
if (!database || !database->isConnected()) {
|
||||||
|
qWarning() << "CounterManager: База данных не подключена";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_database = database;
|
||||||
|
return loadFromDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CounterManager::addCounter(const QString &name, int initialValue)
|
||||||
|
{
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
qWarning() << "CounterManager: Нельзя добавить счётчик с пустым именем";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contains(name)) {
|
||||||
|
qWarning() << "CounterManager: Счётчик с именем" << name << "уже существует";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Counter newCounter(name, initialValue);
|
||||||
|
m_counters.append(newCounter);
|
||||||
|
|
||||||
|
if (!saveToDatabase()) {
|
||||||
|
m_counters.removeLast();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit counterAdded(name, initialValue);
|
||||||
|
emit dataChanged();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CounterManager::removeCounter(const QString &name)
|
||||||
|
{
|
||||||
|
int index = findIndex(name);
|
||||||
|
if (index == -1) {
|
||||||
|
qWarning() << "CounterManager: Счётчик" << name << "не найден";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Counter removed = m_counters.at(index);
|
||||||
|
m_counters.removeAt(index);
|
||||||
|
|
||||||
|
if (!saveToDatabase()) {
|
||||||
|
m_counters.insert(index, removed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit counterRemoved(name);
|
||||||
|
emit dataChanged();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CounterManager::updateCounter(const QString &oldName, const QString &newName, int newValue)
|
||||||
|
{
|
||||||
|
int index = findIndex(oldName);
|
||||||
|
if (index == -1) {
|
||||||
|
qWarning() << "CounterManager: Счётчик" << oldName << "не найден";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldName != newName && contains(newName)) {
|
||||||
|
qWarning() << "CounterManager: Имя" << newName << "уже занято";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Counter oldCounter = m_counters.at(index);
|
||||||
|
m_counters[index].name = newName;
|
||||||
|
m_counters[index].count = newValue;
|
||||||
|
|
||||||
|
if (!saveToDatabase()) {
|
||||||
|
m_counters[index] = oldCounter;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit counterUpdated(oldName, newName);
|
||||||
|
emit dataChanged();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CounterManager::incrementCounter(const QString &name, int step)
|
||||||
|
{
|
||||||
|
int index = findIndex(name);
|
||||||
|
if (index == -1) return false;
|
||||||
|
|
||||||
|
m_counters[index].count += step;
|
||||||
|
if (!saveToDatabase()) {
|
||||||
|
m_counters[index].count -= step; // откат
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit counterIncremented(name, m_counters[index].count);
|
||||||
|
emit dataChanged();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CounterManager::decrementCounter(const QString &name, int step)
|
||||||
|
{
|
||||||
|
return incrementCounter(name, -step);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CounterManager::getCount(const QString &name) const
|
||||||
|
{
|
||||||
|
int index = findIndex(name);
|
||||||
|
return (index != -1) ? m_counters.at(index).count : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CounterManager::processMessage(const QString &message)
|
||||||
|
{
|
||||||
|
for (const Counter &c : m_counters) {
|
||||||
|
if (message.contains(c.name, Qt::CaseInsensitive)) {
|
||||||
|
// Увеличиваем счётчик, но без повторного сохранения после каждого (один общий save)
|
||||||
|
// Здесь мы меняем значение, но сохраним после цикла, чтобы не делать много запросов
|
||||||
|
// Для простоты используем incrementCounter, который сам сохраняет, но это будет много раз.
|
||||||
|
// Лучше собрать имена, которые нужно увеличить, а потом применить.
|
||||||
|
// Но в данном случае можно просто вызывать incrementCounter, это не критично.
|
||||||
|
incrementCounter(c.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CounterManager::contains(const QString &name) const
|
||||||
|
{
|
||||||
|
return findIndex(name) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CounterManager::saveToDatabase()
|
||||||
|
{
|
||||||
|
if (!m_database) {
|
||||||
|
qWarning() << "CounterManager: База данных не установлена";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTableWidget tempTable;
|
||||||
|
QStringList headers = {"Имя", "Значение"};
|
||||||
|
tempTable.setColumnCount(2);
|
||||||
|
tempTable.setHorizontalHeaderLabels(headers);
|
||||||
|
tempTable.setRowCount(m_counters.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < m_counters.size(); ++i) {
|
||||||
|
const Counter &c = m_counters.at(i);
|
||||||
|
tempTable.setItem(i, 0, new QTableWidgetItem(c.name));
|
||||||
|
tempTable.setItem(i, 1, new QTableWidgetItem(QString::number(c.count)));
|
||||||
|
}
|
||||||
|
tempTable.setObjectName("sgCounters");
|
||||||
|
// Сохраняем через общий метод (предполагаем, что таблица в БД называется "sgCounters")
|
||||||
|
m_database->SaveTableWidget(&tempTable);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CounterManager::loadFromDatabase()
|
||||||
|
{
|
||||||
|
if (!m_database) {
|
||||||
|
qWarning() << "CounterManager: База данных не установлена";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTableWidget tempTable;
|
||||||
|
QStringList headers = {"Имя", "Значение"};
|
||||||
|
tempTable.setColumnCount(2);
|
||||||
|
tempTable.setHorizontalHeaderLabels(headers);
|
||||||
|
tempTable.setObjectName("sgCounters");
|
||||||
|
m_database->LoadTableWidget(&tempTable);
|
||||||
|
|
||||||
|
m_counters.clear();
|
||||||
|
for (int row = 0; row < tempTable.rowCount(); ++row) {
|
||||||
|
QTableWidgetItem *nameItem = tempTable.item(row, 0);
|
||||||
|
QTableWidgetItem *valueItem = tempTable.item(row, 1);
|
||||||
|
|
||||||
|
if (!nameItem || !valueItem) continue;
|
||||||
|
|
||||||
|
QString name = nameItem->text().trimmed();
|
||||||
|
if (name.isEmpty()) continue;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
int value = valueItem->text().toInt(&ok);
|
||||||
|
if (!ok) continue;
|
||||||
|
|
||||||
|
m_counters.append(Counter(name, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
emit dataChanged();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CounterManager::findIndex(const QString &name) const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_counters.size(); ++i) {
|
||||||
|
if (m_counters.at(i).name == name)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
// countermanager.h
|
||||||
|
#ifndef COUNTERMANAGER_H
|
||||||
|
#define COUNTERMANAGER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QString>
|
||||||
|
#include "udatabase.h"
|
||||||
|
|
||||||
|
class CounterManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct Counter {
|
||||||
|
QString name;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
Counter() : count(0) {}
|
||||||
|
Counter(const QString& n, int c = 0) : name(n), count(c) {}
|
||||||
|
|
||||||
|
bool operator==(const QString& otherName) const { return name == otherName; }
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit CounterManager(QObject *parent = nullptr);
|
||||||
|
~CounterManager();
|
||||||
|
|
||||||
|
// Инициализация с базой данных
|
||||||
|
bool initialize(uDataBase *database);
|
||||||
|
|
||||||
|
// Управление счетчиками
|
||||||
|
bool addCounter(const QString &name, int initialValue = 0);
|
||||||
|
bool removeCounter(const QString &name);
|
||||||
|
bool updateCounter(const QString &oldName, const QString &newName, int newValue);
|
||||||
|
bool incrementCounter(const QString &name, int step = 1);
|
||||||
|
bool decrementCounter(const QString &name, int step = 1);
|
||||||
|
int getCount(const QString &name) const;
|
||||||
|
|
||||||
|
// Обработка сообщения: увеличивает счётчики, чьи имена встречаются в тексте
|
||||||
|
void processMessage(const QString &message);
|
||||||
|
|
||||||
|
// Получение всех счётчиков
|
||||||
|
QVector<Counter> getAllCounters() const { return m_counters; }
|
||||||
|
bool contains(const QString &name) const;
|
||||||
|
|
||||||
|
// Сохранение/загрузка в БД
|
||||||
|
bool saveToDatabase();
|
||||||
|
bool loadFromDatabase();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void dataChanged();
|
||||||
|
void counterAdded(const QString &name, int value);
|
||||||
|
void counterRemoved(const QString &name);
|
||||||
|
void counterUpdated(const QString &oldName, const QString &newName);
|
||||||
|
void counterIncremented(const QString &name, int newValue);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uDataBase *m_database;
|
||||||
|
QVector<Counter> m_counters;
|
||||||
|
|
||||||
|
int findIndex(const QString &name) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COUNTERMANAGER_H
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonValue>
|
#include <QJsonValue>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
// EmoteProvider implementation
|
// EmoteProvider implementation
|
||||||
EmoteProvider::EmoteProvider(QObject *parent)
|
EmoteProvider::EmoteProvider(QObject *parent)
|
||||||
@@ -416,3 +418,52 @@ void SevenTVProvider::parseCustomResponse(const QByteArray &data, const QString
|
|||||||
QString("Loaded %1 custom emotes for user %2").arg(m_emotes.size()).arg(userId),
|
QString("Loaded %1 custom emotes for user %2").arg(m_emotes.size()).arg(userId),
|
||||||
LOG_INFO);
|
LOG_INFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString BTTVProvider::cleanMessage(const QString& message) const
|
||||||
|
{
|
||||||
|
if (m_emotes.isEmpty())
|
||||||
|
return message;
|
||||||
|
|
||||||
|
// Собираем все коды эмоций в множество для быстрого поиска
|
||||||
|
QSet<QString> codes;
|
||||||
|
for (const BTTVEmote& emote : m_emotes) {
|
||||||
|
codes.insert(emote.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Разбиваем сообщение на токены (слова и не-слова)
|
||||||
|
QRegularExpression wordSplitter("(\\w+|[^\\w]+)");
|
||||||
|
QRegularExpressionMatchIterator it = wordSplitter.globalMatch(message);
|
||||||
|
QStringList parts;
|
||||||
|
while (it.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = it.next();
|
||||||
|
QString token = match.captured(0);
|
||||||
|
// Если токен состоит только из букв/цифр и является кодом эмоции – пропускаем
|
||||||
|
if (token[0].isLetterOrNumber() && codes.contains(token))
|
||||||
|
continue;
|
||||||
|
parts.append(token);
|
||||||
|
}
|
||||||
|
return parts.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SevenTVProvider::cleanMessage(const QString& message) const
|
||||||
|
{
|
||||||
|
if (m_emotes.isEmpty())
|
||||||
|
return message;
|
||||||
|
|
||||||
|
QSet<QString> codes;
|
||||||
|
for (const SevenTVEmote& emote : m_emotes) {
|
||||||
|
codes.insert(emote.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRegularExpression wordSplitter("(\\w+|[^\\w]+)");
|
||||||
|
QRegularExpressionMatchIterator it = wordSplitter.globalMatch(message);
|
||||||
|
QStringList parts;
|
||||||
|
while (it.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = it.next();
|
||||||
|
QString token = match.captured(0);
|
||||||
|
if (token[0].isLetterOrNumber() && codes.contains(token))
|
||||||
|
continue;
|
||||||
|
parts.append(token);
|
||||||
|
}
|
||||||
|
return parts.join("");
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ public:
|
|||||||
virtual void fetchGlobal() = 0;
|
virtual void fetchGlobal() = 0;
|
||||||
virtual void fetchCustom(const QString &userId) = 0;
|
virtual void fetchCustom(const QString &userId) = 0;
|
||||||
virtual QString getEmoteUrl(const QString &emoteName) = 0;
|
virtual QString getEmoteUrl(const QString &emoteName) = 0;
|
||||||
|
virtual QString cleanMessage(const QString& message) const = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void emotesLoaded();
|
void emotesLoaded();
|
||||||
@@ -71,6 +72,7 @@ public:
|
|||||||
void fetchGlobal() override;
|
void fetchGlobal() override;
|
||||||
void fetchCustom(const QString &userId) override;
|
void fetchCustom(const QString &userId) override;
|
||||||
QString getEmoteUrl(const QString &emoteName) override;
|
QString getEmoteUrl(const QString &emoteName) override;
|
||||||
|
QString cleanMessage(const QString& message) const override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onGlobalReplyFinished(QNetworkReply *reply);
|
void onGlobalReplyFinished(QNetworkReply *reply);
|
||||||
@@ -95,6 +97,7 @@ public:
|
|||||||
void fetchGlobal() override;
|
void fetchGlobal() override;
|
||||||
void fetchCustom(const QString &userId) override;
|
void fetchCustom(const QString &userId) override;
|
||||||
QString getEmoteUrl(const QString &emoteName) override;
|
QString getEmoteUrl(const QString &emoteName) override;
|
||||||
|
QString cleanMessage(const QString& message) const override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onGlobalReplyFinished(QNetworkReply *reply);
|
void onGlobalReplyFinished(QNetworkReply *reply);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
debug/commandprocessor.o
|
debug/commandprocessor.o
|
||||||
|
debug/countermanager.o
|
||||||
debug/emoteprovider.o
|
debug/emoteprovider.o
|
||||||
debug/fcolorsetting.o
|
debug/fcolorsetting.o
|
||||||
debug/fcreatechat.o
|
debug/fcreatechat.o
|
||||||
@@ -28,6 +29,7 @@ debug/webserverchat.o
|
|||||||
debug/webservernotify.o
|
debug/webservernotify.o
|
||||||
debug/websocketclient.o
|
debug/websocketclient.o
|
||||||
debug/moc_commandprocessor.o
|
debug/moc_commandprocessor.o
|
||||||
|
debug/moc_countermanager.o
|
||||||
debug/moc_emoteprovider.o
|
debug/moc_emoteprovider.o
|
||||||
debug/moc_fcolorsetting.o
|
debug/moc_fcolorsetting.o
|
||||||
debug/moc_fcreatechat.o
|
debug/moc_fcreatechat.o
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
release/commandprocessor.o
|
release/commandprocessor.o
|
||||||
|
release/countermanager.o
|
||||||
release/emoteprovider.o
|
release/emoteprovider.o
|
||||||
release/fcolorsetting.o
|
release/fcolorsetting.o
|
||||||
release/fcreatechat.o
|
release/fcreatechat.o
|
||||||
@@ -28,6 +29,7 @@ release/webserverchat.o
|
|||||||
release/webservernotify.o
|
release/webservernotify.o
|
||||||
release/websocketclient.o
|
release/websocketclient.o
|
||||||
release/moc_commandprocessor.o
|
release/moc_commandprocessor.o
|
||||||
|
release/moc_countermanager.o
|
||||||
release/moc_emoteprovider.o
|
release/moc_emoteprovider.o
|
||||||
release/moc_fcolorsetting.o
|
release/moc_fcolorsetting.o
|
||||||
release/moc_fcreatechat.o
|
release/moc_fcreatechat.o
|
||||||
|
|||||||
+172
-8
@@ -407,6 +407,22 @@ void uGeneral::initializeManagers()
|
|||||||
ui->widget_3->setManagerType(FSingleGrid::TemplateManager);
|
ui->widget_3->setManagerType(FSingleGrid::TemplateManager);
|
||||||
ui->widget_3->setDatabase(db);
|
ui->widget_3->setDatabase(db);
|
||||||
|
|
||||||
|
m_counterManager = new CounterManager(this);
|
||||||
|
if (db) {
|
||||||
|
m_counterManager->initialize(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Настраиваем таблицу счётчиков (предполагаем, что она уже есть в .ui с objectName "sgCounters")
|
||||||
|
setupCountersTable();
|
||||||
|
|
||||||
|
// Подключаем сигналы для обновления таблицы при изменениях
|
||||||
|
connect(m_counterManager, &CounterManager::dataChanged, this, &uGeneral::updateCountersTable);
|
||||||
|
connect(m_counterManager, &CounterManager::counterAdded, this, [this](const QString&, int) { updateCountersTable(); });
|
||||||
|
connect(m_counterManager, &CounterManager::counterRemoved, this, [this](const QString&) { updateCountersTable(); });
|
||||||
|
connect(m_counterManager, &CounterManager::counterUpdated, this, [this](const QString&, const QString&) { updateCountersTable(); });
|
||||||
|
connect(m_counterManager, &CounterManager::counterIncremented, this, [this](const QString&, int) { updateCountersTable(); });
|
||||||
|
|
||||||
|
|
||||||
m_commandProcessor = new CommandProcessor(this);
|
m_commandProcessor = new CommandProcessor(this);
|
||||||
|
|
||||||
if (db) {
|
if (db) {
|
||||||
@@ -428,9 +444,46 @@ void uGeneral::initializeManagers()
|
|||||||
context.mediaFileManager = m_SoundFiles;
|
context.mediaFileManager = m_SoundFiles;
|
||||||
context.channel = ui->edtChannel->text();
|
context.channel = ui->edtChannel->text();
|
||||||
context.notifyVolume = ui->tbNotifyVolume->value();
|
context.notifyVolume = ui->tbNotifyVolume->value();
|
||||||
|
context.counterManager = m_counterManager;
|
||||||
m_commandProcessor->setContext(context);
|
m_commandProcessor->setContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void uGeneral::setupCountersTable(){
|
||||||
|
// Предполагаем, что sgCounters уже есть в ui
|
||||||
|
ui->sgCounters->setColumnCount(2);
|
||||||
|
QStringList headers;
|
||||||
|
headers << "Счётчик" << "Значение";
|
||||||
|
ui->sgCounters->setHorizontalHeaderLabels(headers);
|
||||||
|
ui->sgCounters->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
ui->sgCounters->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
ui->sgCounters->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
ui->sgCounters->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
ui->sgCounters->setColumnWidth(0, 200);
|
||||||
|
ui->sgCounters->setColumnWidth(1, 80);
|
||||||
|
|
||||||
|
updateCountersTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void uGeneral::updateCountersTable()
|
||||||
|
{
|
||||||
|
|
||||||
|
ui->sgCounters->setRowCount(0);
|
||||||
|
ui->cbCounters->clear();
|
||||||
|
if (!m_counterManager) return;
|
||||||
|
|
||||||
|
QVector<CounterManager::Counter> counters = m_counterManager->getAllCounters();
|
||||||
|
for (const auto &c : counters) {
|
||||||
|
int row = ui->sgCounters->rowCount();
|
||||||
|
ui->sgCounters->insertRow(row);
|
||||||
|
ui->sgCounters->setItem(row, 0, new QTableWidgetItem(c.name));
|
||||||
|
ui->sgCounters->setItem(row, 1, new QTableWidgetItem(QString::number(c.count)));
|
||||||
|
ui->cbCounters->addItem(c.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void uGeneral::loadCommandsFromTableWidget()
|
void uGeneral::loadCommandsFromTableWidget()
|
||||||
@@ -742,6 +795,7 @@ uGeneral::~uGeneral()
|
|||||||
delete m_neuralTemplateManager;
|
delete m_neuralTemplateManager;
|
||||||
m_neuralTemplateManager = nullptr;
|
m_neuralTemplateManager = nullptr;
|
||||||
}
|
}
|
||||||
|
delete m_counterManager;
|
||||||
delete m_createNotifyDialog;
|
delete m_createNotifyDialog;
|
||||||
delete m_createChatDialog;
|
delete m_createChatDialog;
|
||||||
delete ui;
|
delete ui;
|
||||||
@@ -1740,7 +1794,8 @@ void uGeneral::initializeNotificationSounds()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void uGeneral::handleNewMessage(const QString &message){
|
void uGeneral::handleNewMessage(const QString &message)
|
||||||
|
{
|
||||||
TwitchMessage msg = TwitchMessage::parse(message);
|
TwitchMessage msg = TwitchMessage::parse(message);
|
||||||
|
|
||||||
QString userId = m_userManager->checkUser(msg.displayName, msg);
|
QString userId = m_userManager->checkUser(msg.displayName, msg);
|
||||||
@@ -1754,8 +1809,30 @@ void uGeneral::handleNewMessage(const QString &message){
|
|||||||
|
|
||||||
QString processedMessage = processTwitchMessage(msg);
|
QString processedMessage = processTwitchMessage(msg);
|
||||||
QString formattedNickname = formatNicknameWithBadges(msg);
|
QString formattedNickname = formatNicknameWithBadges(msg);
|
||||||
|
|
||||||
addChatMessage(formattedNickname, processedMessage);
|
addChatMessage(formattedNickname, processedMessage);
|
||||||
|
if (m_counterManager) {
|
||||||
|
m_counterManager->processMessage(msg.message); // или cleanedText
|
||||||
|
}
|
||||||
|
QString cleanedText = cleanMessageFromAllEmotes(msg.message);
|
||||||
|
// Удаляем ссылки (опционально)
|
||||||
|
cleanedText.remove(QRegularExpression("https?://\\S+"));
|
||||||
|
cleanedText = cleanedText.trimmed();
|
||||||
|
|
||||||
|
// Проверяем наличие русских букв
|
||||||
|
bool hasRussian = false;
|
||||||
|
for (const QChar& ch : cleanedText) {
|
||||||
|
if (ch.unicode() >= 0x0400 && ch.unicode() <= 0x04FF) {
|
||||||
|
hasRussian = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasRussian && !cleanedText.isEmpty()) {
|
||||||
|
// Здесь нужно вызвать перевод (например, через API)
|
||||||
|
// sendToTranslate(cleanedText);
|
||||||
|
LogManager::instance()->debug("uGeneral", "handleNewMessage",
|
||||||
|
"Требуется перевод: " + cleanedText);
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.message.startsWith("!!!")) {
|
if (msg.message.startsWith("!!!")) {
|
||||||
QString cleanedMessage = msg.message.mid(3);
|
QString cleanedMessage = msg.message.mid(3);
|
||||||
@@ -1766,6 +1843,14 @@ void uGeneral::handleNewMessage(const QString &message){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString uGeneral::cleanMessageFromAllEmotes(const QString& message) const
|
||||||
|
{
|
||||||
|
QString cleaned = message;
|
||||||
|
cleaned = bttvProvider.cleanMessage(cleaned);
|
||||||
|
cleaned = sevenTVProvider.cleanMessage(cleaned);
|
||||||
|
return cleaned;
|
||||||
|
}
|
||||||
|
|
||||||
void uGeneral::processUserCommand(const QString &username, const QString &commandText)
|
void uGeneral::processUserCommand(const QString &username, const QString &commandText)
|
||||||
{
|
{
|
||||||
if (!m_commandProcessor || !m_userManager) return;
|
if (!m_commandProcessor || !m_userManager) return;
|
||||||
@@ -1972,11 +2057,6 @@ void uGeneral::disconnectFromTwitch()
|
|||||||
setTwitchConnected(false);
|
setTwitchConnected(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uGeneral::execCommand(const QString &sender, const QString &message)
|
|
||||||
{
|
|
||||||
LogManager::instance()->debug("uGeneral", "execCommand",
|
|
||||||
QString("Обработка команды от %1: %2").arg(sender).arg(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
void uGeneral::on_lbRandomGroup_itemClicked(QListWidgetItem *item)
|
void uGeneral::on_lbRandomGroup_itemClicked(QListWidgetItem *item)
|
||||||
{
|
{
|
||||||
@@ -3026,3 +3106,87 @@ void uGeneral::on_btnRmWebService_clicked()
|
|||||||
.arg(serviceName));
|
.arg(serviceName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uGeneral::on_sgCounters_cellClicked(int row, int column)
|
||||||
|
{
|
||||||
|
Q_UNUSED(column);
|
||||||
|
QString name = ui->sgCounters->item(row, 0)->text();
|
||||||
|
int value = ui->sgCounters->item(row, 1)->text().toInt();
|
||||||
|
ui->edtWordCounter->setText(name);
|
||||||
|
ui->sbStartCounter->setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uGeneral::on_sgCounters_cellDoubleClicked(int row, int column)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uGeneral::on_btnCounterAdd_clicked()
|
||||||
|
{
|
||||||
|
QString name = ui->edtWordCounter->text().trimmed(); // предположим, есть поле ввода
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Введите название счётчика!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int initial = ui->sbStartCounter->value(); // спинбокс для начального значения
|
||||||
|
if (m_counterManager->addCounter(name, initial)) {
|
||||||
|
ui->edtWordCounter->clear();
|
||||||
|
ui->sbStartCounter->setValue(0);
|
||||||
|
updateCountersTable();
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Не удалось добавить счётчик (возможно, уже существует)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uGeneral::on_btnCounterDelete_clicked()
|
||||||
|
{
|
||||||
|
int row = ui->sgCounters->currentRow();
|
||||||
|
if (row < 0) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Выберите счётчик для удаления!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name = ui->sgCounters->item(row, 0)->text();
|
||||||
|
if (m_counterManager->removeCounter(name)) {
|
||||||
|
updateCountersTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uGeneral::on_btnCounterEdit_clicked()
|
||||||
|
{
|
||||||
|
int row = ui->sgCounters->currentRow();
|
||||||
|
if (row < 0) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Выберите счётчик для редактирования!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString oldName = ui->sgCounters->item(row, 0)->text();
|
||||||
|
QString newName = ui->edtWordCounter->text().trimmed();
|
||||||
|
int newValue = ui->sbStartCounter->value();
|
||||||
|
|
||||||
|
if (newName.isEmpty()) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Введите новое название счётчика!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_counterManager->updateCounter(oldName, newName, newValue)) {
|
||||||
|
ui->edtWordCounter->clear();
|
||||||
|
ui->sbStartCounter->setValue(0);
|
||||||
|
updateCountersTable();
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Не удалось обновить счётчик");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uGeneral::on_btnCounterAtoText_clicked()
|
||||||
|
{
|
||||||
|
QTextCursor cursor = ui->textBrowser->textCursor();
|
||||||
|
cursor.insertText("|)" + ui->cbCounters->currentText() + "|)");
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-7
@@ -10,6 +10,7 @@
|
|||||||
#include <udatabase.h>
|
#include <udatabase.h>
|
||||||
#include <soundmanager.h>
|
#include <soundmanager.h>
|
||||||
#include "commandprocessor.h"
|
#include "commandprocessor.h"
|
||||||
|
#include "countermanager.h"
|
||||||
#include "fcreatechat.h"
|
#include "fcreatechat.h"
|
||||||
#include "fcreatenotify.h"
|
#include "fcreatenotify.h"
|
||||||
#include "logmanager.h"
|
#include "logmanager.h"
|
||||||
@@ -141,11 +142,6 @@ private slots:
|
|||||||
void handleConnected();
|
void handleConnected();
|
||||||
void handleDisconnected();
|
void handleDisconnected();
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// СЛОТЫ ДЛЯ РАБОТЫ С КОМАНДАМИ И ОТВЕТАМИ
|
|
||||||
// ========================================================================
|
|
||||||
void execCommand(const QString &sender, const QString &message);
|
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// СЛОТЫ ДЛЯ РАБОТЫ С ИСКУССТВЕННЫМ ИНТЕЛЛЕКТОМ
|
// СЛОТЫ ДЛЯ РАБОТЫ С ИСКУССТВЕННЫМ ИНТЕЛЛЕКТОМ
|
||||||
@@ -346,6 +342,18 @@ private slots:
|
|||||||
|
|
||||||
void on_btnRmWebService_clicked();
|
void on_btnRmWebService_clicked();
|
||||||
|
|
||||||
|
void on_sgCounters_cellClicked(int row, int column);
|
||||||
|
|
||||||
|
void on_sgCounters_cellDoubleClicked(int row, int column);
|
||||||
|
|
||||||
|
void on_btnCounterAdd_clicked();
|
||||||
|
|
||||||
|
void on_btnCounterDelete_clicked();
|
||||||
|
|
||||||
|
void on_btnCounterEdit_clicked();
|
||||||
|
|
||||||
|
void on_btnCounterAtoText_clicked();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// Установка статуса подключения к Twitch
|
// Установка статуса подключения к Twitch
|
||||||
void setTwitchConnected(bool connected);
|
void setTwitchConnected(bool connected);
|
||||||
@@ -363,6 +371,7 @@ private:
|
|||||||
uLink *fLinkForm; // Форма ссылок
|
uLink *fLinkForm; // Форма ссылок
|
||||||
TTTVAuth *TTVAuth; // Данные авторизации Twitch
|
TTTVAuth *TTVAuth; // Данные авторизации Twitch
|
||||||
UserManager *m_userManager; // Менеджер пользователей
|
UserManager *m_userManager; // Менеджер пользователей
|
||||||
|
CounterManager *m_counterManager;
|
||||||
CommandProcessor* m_commandProcessor; // Процессор команд
|
CommandProcessor* m_commandProcessor; // Процессор команд
|
||||||
WebSocketClient *m_twitchClient; // WebSocket клиент для Twitch
|
WebSocketClient *m_twitchClient; // WebSocket клиент для Twitch
|
||||||
UserWidget* m_userWidget; // Виджет пользователя
|
UserWidget* m_userWidget; // Виджет пользователя
|
||||||
@@ -405,8 +414,8 @@ private:
|
|||||||
int findNotificationServerRow(HttpServer *server);
|
int findNotificationServerRow(HttpServer *server);
|
||||||
int findChatServerRow(HttpServerChat *server);
|
int findChatServerRow(HttpServerChat *server);
|
||||||
QString generateServerId() const;
|
QString generateServerId() const;
|
||||||
|
void setupCountersTable();
|
||||||
|
void updateCountersTable();
|
||||||
// Текущие настройки для формы
|
// Текущие настройки для формы
|
||||||
QVariantMap m_currentSettings;
|
QVariantMap m_currentSettings;
|
||||||
|
|
||||||
@@ -481,6 +490,8 @@ private:
|
|||||||
void loadNeuralTemplatesFromTableWidget();
|
void loadNeuralTemplatesFromTableWidget();
|
||||||
void processUserCommand(const QString &username, const QString &commandText);
|
void processUserCommand(const QString &username, const QString &commandText);
|
||||||
void sendChatResponse(const QString &response);
|
void sendChatResponse(const QString &response);
|
||||||
|
|
||||||
|
QString cleanMessageFromAllEmotes(const QString& message) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // UGENERAL_H
|
#endif // UGENERAL_H
|
||||||
|
|||||||
+130
-1
@@ -42,7 +42,7 @@
|
|||||||
<enum>Qt::LeftToRight</enum>
|
<enum>Qt::LeftToRight</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>6</number>
|
<number>2</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="tabsClosable">
|
<property name="tabsClosable">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
@@ -974,6 +974,9 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="1" colspan="2">
|
||||||
|
<widget class="QComboBox" name="cbCounters"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@@ -1416,6 +1419,11 @@
|
|||||||
<string>Автоматика</string>
|
<string>Автоматика</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_tab_auto">
|
<layout class="QVBoxLayout" name="verticalLayout_tab_auto">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_10">
|
<widget class="QGroupBox" name="groupBox_10">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@@ -1553,6 +1561,127 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_11">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTipDuration">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Счетчики</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_26">
|
||||||
|
<item>
|
||||||
|
<widget class="QTableWidget" name="sgCounters">
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Слово или фраза</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Количество</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_24">
|
||||||
|
<property name="text">
|
||||||
|
<string>Слово:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="edtWordCounter"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_25">
|
||||||
|
<property name="text">
|
||||||
|
<string>Количество:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="sbStartCounter"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_9">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_16">
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnCounterAdd">
|
||||||
|
<property name="text">
|
||||||
|
<string>Добавить</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnCounterEdit">
|
||||||
|
<property name="text">
|
||||||
|
<string>Изменить</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnCounterDelete">
|
||||||
|
<property name="text">
|
||||||
|
<string>Удалить</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_10">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer_auto">
|
<spacer name="verticalSpacer_auto">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|||||||
Reference in New Issue
Block a user