Refactor UI and database logic for device management
Replaced `MyWindow` with `MainWindow` featuring dynamic device widgets. Introduced `DeviceInfo`, `DeviceDAO`, and improved database interaction. Added flow layout and refactored image handling for better performance.
This commit is contained in:
parent
1672216bdc
commit
4d9889db46
@ -41,11 +41,17 @@ file(GLOB_RECURSE SERVICES_SOURCES
|
|||||||
"${CMAKE_CURRENT_SOURCE_DIR}/services/*.h"
|
"${CMAKE_CURRENT_SOURCE_DIR}/services/*.h"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE MODELS_SOURCES
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/models/*.cpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/models/*.h"
|
||||||
|
)
|
||||||
|
|
||||||
add_executable(ARCDesktopProject
|
add_executable(ARCDesktopProject
|
||||||
main.cpp
|
main.cpp
|
||||||
${VIEW_SOURCES}
|
${VIEW_SOURCES}
|
||||||
${SERVICES_SOURCES}
|
${SERVICES_SOURCES}
|
||||||
${DB_SOURCES}
|
${DB_SOURCES}
|
||||||
|
${MODELS_SOURCES}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
@ -66,3 +72,10 @@ elseif (APPLE)
|
|||||||
tesseract
|
tesseract
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
target_include_directories(ARCDesktopProject PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/views
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/database
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/services
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/models
|
||||||
|
)
|
||||||
@ -1,4 +1,4 @@
|
|||||||
#include "database_manager.h"
|
#include "DatabaseManager.h"
|
||||||
#include <QSqlError>
|
#include <QSqlError>
|
||||||
#include <QtCore/qcoreapplication.h>
|
#include <QtCore/qcoreapplication.h>
|
||||||
|
|
||||||
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
#include "common_data_dao.h"
|
#include "CommonDataDAO.h"
|
||||||
#include "../database_manager.h"
|
#include "DatabaseManager.h"
|
||||||
|
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
#include <QSqlError>
|
#include <QSqlError>
|
||||||
139
database/dao/DeviceDAO.cpp
Normal file
139
database/dao/DeviceDAO.cpp
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include "DeviceDAO.h"
|
||||||
|
#include "DatabaseManager.h"
|
||||||
|
#include "DeviceInfo.h"
|
||||||
|
|
||||||
|
QList<DeviceInfo> DeviceDAO::getAllDevices() {
|
||||||
|
QList<DeviceInfo> devices;
|
||||||
|
|
||||||
|
QSqlQuery query(DatabaseManager::instance().database());
|
||||||
|
query.prepare(
|
||||||
|
"SELECT id, status, name, android, screen_width, screen_height, battery, updateTime, image FROM devices WHERE image IS NOT NULL AND status = 'device'");
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
qWarning() << "Ошибка при получении списка устройств:" << query.lastError().text();
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (query.next()) {
|
||||||
|
DeviceInfo device;
|
||||||
|
device.id = query.value("id").toString();
|
||||||
|
device.status = query.value("status").toString();
|
||||||
|
device.name = query.value("name").toString();
|
||||||
|
device.android = query.value("android").toString();
|
||||||
|
device.screenWidth = query.value("screen_width").toInt();
|
||||||
|
device.screenHeight = query.value("screen_height").toInt();
|
||||||
|
device.battery = query.value("battery").toInt();
|
||||||
|
device.updateTime = QDateTime::fromString(query.value("updateTime").toString(), Qt::ISODate);
|
||||||
|
device.image = query.value("image").toByteArray();
|
||||||
|
|
||||||
|
devices.append(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceDAO::upsertDevice(const DeviceInfo &device) {
|
||||||
|
QSqlQuery query(DatabaseManager::instance().database());
|
||||||
|
|
||||||
|
// Проверка: существует ли запись
|
||||||
|
query.prepare("SELECT COUNT(*) FROM devices WHERE id = :id");
|
||||||
|
query.bindValue(":id", device.id);
|
||||||
|
|
||||||
|
if (!query.exec() || !query.next()) {
|
||||||
|
qWarning() << "Ошибка при проверке существования записи:" << query.lastError().text();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool exists = query.value(0).toInt() > 0;
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
// Обновление
|
||||||
|
query.prepare(R"(
|
||||||
|
UPDATE devices SET
|
||||||
|
status = :status,
|
||||||
|
name = :name,
|
||||||
|
android = :android,
|
||||||
|
screen_width = :width,
|
||||||
|
screen_height = :height,
|
||||||
|
battery = :battery,
|
||||||
|
updateTime = :updateTime
|
||||||
|
WHERE id = :id
|
||||||
|
)");
|
||||||
|
} else {
|
||||||
|
// Вставка
|
||||||
|
query.prepare(R"(
|
||||||
|
INSERT INTO devices (
|
||||||
|
id, status, name, android, screen_width, screen_height, battery, updateTime
|
||||||
|
) VALUES (
|
||||||
|
:id, :status, :name, :android, :width, :height, :battery, :updateTime
|
||||||
|
)
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
query.bindValue(":id", device.id);
|
||||||
|
query.bindValue(":status", device.status);
|
||||||
|
query.bindValue(":name", device.name);
|
||||||
|
query.bindValue(":android", device.android);
|
||||||
|
query.bindValue(":width", device.screenWidth);
|
||||||
|
query.bindValue(":height", device.screenHeight);
|
||||||
|
query.bindValue(":battery", device.battery);
|
||||||
|
query.bindValue(":updateTime", device.updateTime.toString(Qt::ISODate));
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
qWarning() << "Ошибка при вставке/обновлении устройства:" << query.lastError().text();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceDAO::markDevicesOfflineExcept(const QStringList &onlineDeviceIds) {
|
||||||
|
QSqlQuery query(DatabaseManager::instance().database());
|
||||||
|
|
||||||
|
// Если список ID пуст, помечаем все как offline
|
||||||
|
if (onlineDeviceIds.isEmpty()) {
|
||||||
|
if (!query.exec("UPDATE devices SET status = 'offline'")) {
|
||||||
|
qWarning() << "Ошибка при обновлении устройств на offline:" << query.lastError().text();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генерируем плейсхолдеры :id0, :id1, ...
|
||||||
|
QStringList placeholders;
|
||||||
|
for (int i = 0; i < onlineDeviceIds.size(); ++i) {
|
||||||
|
placeholders << QString(":id%1").arg(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString queryString = QString(
|
||||||
|
"UPDATE devices SET status = 'offline' WHERE id NOT IN (%1)"
|
||||||
|
).arg(placeholders.join(", "));
|
||||||
|
|
||||||
|
query.prepare(queryString);
|
||||||
|
// Привязываем значения к плейсхолдерам
|
||||||
|
for (int i = 0; i < onlineDeviceIds.size(); ++i) {
|
||||||
|
query.bindValue(QString(":id%1").arg(i), onlineDeviceIds[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
qWarning() << "Ошибка при обновлении устройств на offline (исключая online):" << query.lastError().text();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceDAO::updateImage(const QString &deviceId, const QByteArray &imageData) {
|
||||||
|
QSqlQuery query(DatabaseManager::instance().database());
|
||||||
|
query.prepare("UPDATE devices SET image = :image WHERE id = :id");
|
||||||
|
query.bindValue(":image", imageData);
|
||||||
|
query.bindValue(":id", deviceId);
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
qWarning() << "Ошибка при обновлении изображения:" << query.lastError().text();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
14
database/dao/DeviceDAO.h
Normal file
14
database/dao/DeviceDAO.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <DeviceInfo.h>
|
||||||
|
|
||||||
|
class DeviceDAO {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] static bool upsertDevice(const DeviceInfo &device);
|
||||||
|
|
||||||
|
[[nodiscard]] static bool markDevicesOfflineExcept(const QStringList &onlineDeviceIds);
|
||||||
|
|
||||||
|
[[nodiscard]] static bool updateImage(const QString &deviceId, const QByteArray &imageData);
|
||||||
|
|
||||||
|
[[nodiscard]] static QList<DeviceInfo> getAllDevices();
|
||||||
|
};
|
||||||
@ -1,75 +0,0 @@
|
|||||||
#include "device_dao.h"
|
|
||||||
#include <QSqlQuery>
|
|
||||||
#include <QSqlError>
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QDebug>
|
|
||||||
#include "../database_manager.h"
|
|
||||||
|
|
||||||
bool DeviceDAO::upsertDevice(const DeviceInfo& device) {
|
|
||||||
QSqlQuery query(DatabaseManager::instance().database());
|
|
||||||
|
|
||||||
// Проверка: существует ли запись
|
|
||||||
query.prepare("SELECT COUNT(*) FROM devices WHERE id = :id");
|
|
||||||
query.bindValue(":id", device.id);
|
|
||||||
|
|
||||||
if (!query.exec() || !query.next()) {
|
|
||||||
qWarning() << "Ошибка при проверке существования записи:" << query.lastError().text();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool exists = query.value(0).toInt() > 0;
|
|
||||||
|
|
||||||
if (exists) {
|
|
||||||
// Обновление
|
|
||||||
query.prepare(R"(
|
|
||||||
UPDATE devices SET
|
|
||||||
status = :status,
|
|
||||||
name = :name,
|
|
||||||
android = :android,
|
|
||||||
screen_width = :width,
|
|
||||||
screen_height = :height,
|
|
||||||
battery = :battery,
|
|
||||||
updateTime = :updateTime
|
|
||||||
WHERE id = :id
|
|
||||||
)");
|
|
||||||
} else {
|
|
||||||
// Вставка
|
|
||||||
query.prepare(R"(
|
|
||||||
INSERT INTO devices (
|
|
||||||
id, status, name, android, screen_width, screen_height, battery, updateTime
|
|
||||||
) VALUES (
|
|
||||||
:id, :status, :name, :android, :width, :height, :battery, :updateTime
|
|
||||||
)
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
query.bindValue(":id", device.id);
|
|
||||||
query.bindValue(":status", device.status);
|
|
||||||
query.bindValue(":name", device.name);
|
|
||||||
query.bindValue(":android", device.android);
|
|
||||||
query.bindValue(":width", device.screenWidth);
|
|
||||||
query.bindValue(":height", device.screenHeight);
|
|
||||||
query.bindValue(":battery", device.battery);
|
|
||||||
query.bindValue(":updateTime", device.updateTime.toString(Qt::ISODate));
|
|
||||||
|
|
||||||
if (!query.exec()) {
|
|
||||||
qWarning() << "Ошибка при вставке/обновлении устройства:" << query.lastError().text();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DeviceDAO::updateImage(const QString& deviceId, const QByteArray& imageData) {
|
|
||||||
QSqlQuery query(DatabaseManager::instance().database());
|
|
||||||
query.prepare("UPDATE devices SET image = :image WHERE id = :id");
|
|
||||||
query.bindValue(":image", imageData);
|
|
||||||
query.bindValue(":id", deviceId);
|
|
||||||
|
|
||||||
if (!query.exec()) {
|
|
||||||
qWarning() << "Ошибка при обновлении изображения:" << query.lastError().text();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QSqlDatabase>
|
|
||||||
|
|
||||||
struct DeviceInfo {
|
|
||||||
QString id;
|
|
||||||
QString status;
|
|
||||||
QString name;
|
|
||||||
QString android;
|
|
||||||
int screenWidth;
|
|
||||||
int screenHeight;
|
|
||||||
int battery;
|
|
||||||
QDateTime updateTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeviceDAO {
|
|
||||||
public:
|
|
||||||
[[nodiscard]] static bool upsertDevice(const DeviceInfo &device);
|
|
||||||
|
|
||||||
[[nodiscard]] static bool updateImage(const QString &deviceId, const QByteArray &imageData);
|
|
||||||
};
|
|
||||||
11
main.cpp
11
main.cpp
@ -1,11 +1,10 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include "views/MyWindow.h"
|
#include "views/MainWindow.h"
|
||||||
#include <tesseract/baseapi.h>
|
|
||||||
|
|
||||||
#include "database/database_manager.h"
|
#include "database/DatabaseManager.h"
|
||||||
#include "database/dao/common_data_dao.h"
|
#include "database/dao/CommonDataDAO.h"
|
||||||
#include "services/device_screener.h"
|
#include "services/DeviceScreener.h"
|
||||||
|
|
||||||
void setupWorkerAndThread(QCoreApplication& app) {
|
void setupWorkerAndThread(QCoreApplication& app) {
|
||||||
auto* thread = new QThread;
|
auto* thread = new QThread;
|
||||||
@ -73,7 +72,7 @@ int main(int argc, char *argv[]) {
|
|||||||
// qDebug() << "Распознанный текст:" << result;
|
// qDebug() << "Распознанный текст:" << result;
|
||||||
|
|
||||||
|
|
||||||
MyWindow window;
|
MainWindow window;
|
||||||
window.show();
|
window.show();
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|||||||
15
models/DeviceInfo.h
Normal file
15
models/DeviceInfo.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
struct DeviceInfo {
|
||||||
|
QString id;
|
||||||
|
QString status;
|
||||||
|
QString name;
|
||||||
|
QString android;
|
||||||
|
int screenWidth;
|
||||||
|
int screenHeight;
|
||||||
|
int battery;
|
||||||
|
QDateTime updateTime;
|
||||||
|
QByteArray image;
|
||||||
|
};
|
||||||
@ -1,10 +1,13 @@
|
|||||||
#include "device_screener.h"
|
#include "DeviceScreener.h"
|
||||||
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QBuffer>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <../../database/dao/device_dao.h>
|
#include <QImage>
|
||||||
|
#include <dao/DeviceDAO.h>
|
||||||
|
#include <DeviceInfo.h>
|
||||||
|
|
||||||
DeviceScreener::DeviceScreener(QObject *parent) : QObject(parent) {
|
DeviceScreener::DeviceScreener(QObject *parent) : QObject(parent) {
|
||||||
}
|
}
|
||||||
@ -15,26 +18,47 @@ void DeviceScreener::start() {
|
|||||||
qDebug() << "Worker started in thread:" << QThread::currentThread();
|
qDebug() << "Worker started in thread:" << QThread::currentThread();
|
||||||
while (m_running) {
|
while (m_running) {
|
||||||
QList<QPair<QString, QString>> devices = readAdbDevices();
|
QList<QPair<QString, QString>> devices = readAdbDevices();
|
||||||
|
QStringList onlineIds;
|
||||||
for (const auto&[id, status] : devices) {
|
for (const auto&[id, status] : devices) {
|
||||||
qDebug() << "Id:" << id << ", Status:" << status;
|
qDebug() << "Id:" << id << ", Status:" << status;
|
||||||
|
|
||||||
DeviceInfo device;
|
DeviceInfo device;
|
||||||
device.id = id;
|
device.id = id;
|
||||||
device.status = status;
|
|
||||||
device.updateTime = QDateTime::currentDateTime();
|
|
||||||
|
|
||||||
if (status == "device") {
|
if (status == "device") {
|
||||||
|
onlineIds << device.id;
|
||||||
device = readDeviceInfo(id);
|
device = readDeviceInfo(id);
|
||||||
takeScreenshot(id);
|
takeScreenshot(id);
|
||||||
}
|
}
|
||||||
|
device.status = status;
|
||||||
|
device.updateTime = QDateTime::currentDateTime();
|
||||||
qDebug() << "Update device: " << DeviceDAO::upsertDevice(device);
|
qDebug() << "Update device: " << DeviceDAO::upsertDevice(device);
|
||||||
}
|
}
|
||||||
QThread::sleep(5);
|
qDebug() << "All devices offline: " << DeviceDAO::markDevicesOfflineExcept(onlineIds);
|
||||||
|
QThread::sleep(3);
|
||||||
}
|
}
|
||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray convertPngToJpeg(const QByteArray& pngData, const int quality = 90) {
|
||||||
|
QImage image;
|
||||||
|
if (!image.loadFromData(pngData, "PNG")) {
|
||||||
|
qWarning() << "Не удалось загрузить PNG из байт";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!image.save("test.jpg", "JPG", quality)) {
|
||||||
|
// qWarning() << "Не удалось сохранить как JPG:" << "test.jpg";
|
||||||
|
// return {};
|
||||||
|
// }
|
||||||
|
|
||||||
|
QByteArray jpgData;
|
||||||
|
QBuffer buffer(&jpgData);
|
||||||
|
buffer.open(QIODevice::WriteOnly);
|
||||||
|
image.save(&buffer, "JPG", quality);
|
||||||
|
return jpgData;
|
||||||
|
}
|
||||||
|
|
||||||
bool DeviceScreener::takeScreenshot(const QString& deviceId) {
|
bool DeviceScreener::takeScreenshot(const QString& deviceId) {
|
||||||
QProcess process;
|
QProcess process;
|
||||||
QStringList arguments;
|
QStringList arguments;
|
||||||
@ -55,7 +79,7 @@ bool DeviceScreener::takeScreenshot(const QString& deviceId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Обновляем изображение в БД
|
// Обновляем изображение в БД
|
||||||
if (!DeviceDAO::updateImage(deviceId, imageData)) {
|
if (!DeviceDAO::updateImage(deviceId, convertPngToJpeg(imageData, 90))) {
|
||||||
qWarning() << "Не удалось записать изображение в БД для" << deviceId;
|
qWarning() << "Не удалось записать изображение в БД для" << deviceId;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <../../database/dao/device_dao.h>
|
#include <dao/DeviceDAO.h>
|
||||||
|
#include <DeviceInfo.h>
|
||||||
|
|
||||||
class DeviceScreener final : public QObject {
|
class DeviceScreener final : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
37
views/DeviceWidget.cpp
Normal file
37
views/DeviceWidget.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#include "DeviceWidget.h"
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(parent) {
|
||||||
|
auto *layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
this->setStyleSheet("background-color: lightgray;padding: 10px;");
|
||||||
|
|
||||||
|
QImage img;
|
||||||
|
img.loadFromData(device.image, "JPG");
|
||||||
|
|
||||||
|
auto *imageLabel = new QLabel(this);
|
||||||
|
qDebug() << "imageLabel" << img.width() ;
|
||||||
|
imageLabel->setPixmap(QPixmap::fromImage(img).scaled(200, 350, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||||
|
imageLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
layout->addWidget(imageLabel);
|
||||||
|
|
||||||
|
const QString result = QString("%1, Android %2, battery %3% [%4x%5]")
|
||||||
|
.arg(device.name).arg(device.android).arg(device.battery).
|
||||||
|
arg(device.screenWidth).arg(device.screenHeight);
|
||||||
|
|
||||||
|
auto *statusLabel = new QLabel(result, this);
|
||||||
|
statusLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
statusLabel->setMaximumWidth(200);
|
||||||
|
statusLabel->setWordWrap(true);
|
||||||
|
|
||||||
|
const QString styleSheet = QString("color: %1; font-weight: bold;")
|
||||||
|
.arg(device.status == "online" ? "green" : "red");
|
||||||
|
statusLabel->setStyleSheet(styleSheet);
|
||||||
|
|
||||||
|
// layout->setContentsMargins(10, 10, 10, 10);
|
||||||
|
layout->setSpacing(0);
|
||||||
|
layout->addWidget(statusLabel);
|
||||||
|
}
|
||||||
10
views/DeviceWidget.h
Normal file
10
views/DeviceWidget.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <QWidget>
|
||||||
|
#include "DeviceInfo.h"
|
||||||
|
|
||||||
|
class DeviceWidget final : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DeviceWidget(const DeviceInfo& device, QWidget* parent = nullptr);
|
||||||
|
};
|
||||||
63
views/MainWindow.cpp
Normal file
63
views/MainWindow.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "MainWindow.h"
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QScrollArea>
|
||||||
|
|
||||||
|
#include "DeviceInfo.h"
|
||||||
|
#include "DeviceWidget.h"
|
||||||
|
#include "dao/DeviceDAO.h"
|
||||||
|
#include "widget/FlowLayout.h"
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
||||||
|
// QPushButton* btn = new QPushButton("Hello Qt6!", this);
|
||||||
|
// setCentralWidget(btn);
|
||||||
|
|
||||||
|
auto* scrollArea = new QScrollArea(this);
|
||||||
|
auto* contentWidget = new QWidget;
|
||||||
|
flowLayout = new FlowLayout(contentWidget);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
contentWidget->setContentsMargins(0, 0, 0, 0); // Убираем отступы у контейнера
|
||||||
|
flowLayout->setContentsMargins(0, 0, 0, 0); // Убираем отступы у layout
|
||||||
|
flowLayout->setSpacing(0);
|
||||||
|
|
||||||
|
|
||||||
|
QVector<DeviceInfo> devices = DeviceDAO::getAllDevices();
|
||||||
|
for (const auto& device : devices) {
|
||||||
|
auto* deviceWidget = new DeviceWidget(device); // Создаем виджет, используя первый элемент из списка devices
|
||||||
|
deviceWidget->setFixedWidth(220);
|
||||||
|
deviceWidget->setFixedHeight(400);
|
||||||
|
|
||||||
|
deviceWidget->setContentsMargins(0, 0, 0, 0);
|
||||||
|
flowLayout->addWidget(deviceWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(800, 600);
|
||||||
|
|
||||||
|
contentWidget->setLayout(flowLayout);
|
||||||
|
scrollArea->setWidget(contentWidget);
|
||||||
|
scrollArea->setWidgetResizable(true);
|
||||||
|
setCentralWidget(scrollArea);
|
||||||
|
|
||||||
|
timer = new QTimer(this);
|
||||||
|
connect(timer, &QTimer::timeout, this, &MainWindow::loadDevices);
|
||||||
|
timer->start(1000); // каждые 1 сек
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MainWindow::loadDevices() const {
|
||||||
|
const auto devices = DeviceDAO::getAllDevices();
|
||||||
|
|
||||||
|
// Очистить старые виджеты
|
||||||
|
QLayoutItem* item;
|
||||||
|
while ((item = flowLayout->takeAt(0)) != nullptr) {
|
||||||
|
delete item->widget(); // Удаляем виджет
|
||||||
|
delete item; // Удаляем сам элемент из layout
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем новые виджеты для каждого устройства
|
||||||
|
for (const auto& device : devices) {
|
||||||
|
flowLayout->addWidget(new DeviceWidget(device));
|
||||||
|
}
|
||||||
|
}
|
||||||
18
views/MainWindow.h
Normal file
18
views/MainWindow.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QTimer>
|
||||||
|
#include "DeviceInfo.h"
|
||||||
|
#include "widget/FlowLayout.h"
|
||||||
|
|
||||||
|
class MainWindow final : public QMainWindow {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
|
void loadDevices() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWidget* central;
|
||||||
|
FlowLayout* flowLayout;
|
||||||
|
QTimer* timer;
|
||||||
|
};
|
||||||
@ -1,7 +0,0 @@
|
|||||||
#include "MyWindow.h"
|
|
||||||
#include <QPushButton>
|
|
||||||
|
|
||||||
MyWindow::MyWindow(QWidget *parent) : QMainWindow(parent) {
|
|
||||||
QPushButton* btn = new QPushButton("Hello Qt6!", this);
|
|
||||||
setCentralWidget(btn);
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <QMainWindow>
|
|
||||||
|
|
||||||
class MyWindow : public QMainWindow {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
MyWindow(QWidget *parent = nullptr);
|
|
||||||
};
|
|
||||||
130
views/widget/FlowLayout.cpp
Normal file
130
views/widget/FlowLayout.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include "FlowLayout.h"
|
||||||
|
#include <QtWidgets/qwidget.h>
|
||||||
|
|
||||||
|
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing): QLayout(parent), m_hSpace(hSpacing),
|
||||||
|
m_vSpace(vSpacing) {
|
||||||
|
setContentsMargins(margin, margin, margin, margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowLayout::FlowLayout(const int margin, const int hSpacing, const int vSpacing) : m_hSpace(hSpacing),
|
||||||
|
m_vSpace(vSpacing) {
|
||||||
|
setContentsMargins(margin, margin, margin, margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowLayout::~FlowLayout() {
|
||||||
|
QLayoutItem *item;
|
||||||
|
while ((item = takeAt(0)))
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlowLayout::horizontalSpacing() const {
|
||||||
|
if (m_hSpace >= 0) {
|
||||||
|
return m_hSpace;
|
||||||
|
} else {
|
||||||
|
return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlowLayout::verticalSpacing() const {
|
||||||
|
if (m_vSpace >= 0) {
|
||||||
|
return m_vSpace;
|
||||||
|
} else {
|
||||||
|
return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlowLayout::count() const {
|
||||||
|
return itemList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QLayoutItem *FlowLayout::itemAt(int index) const {
|
||||||
|
return itemList.value(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
QLayoutItem *FlowLayout::takeAt(int index) {
|
||||||
|
if (index >= 0 && index < itemList.size())
|
||||||
|
return itemList.takeAt(index);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::Orientations FlowLayout::expandingDirections() const {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlowLayout::hasHeightForWidth() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlowLayout::heightForWidth(int width) const {
|
||||||
|
int height = doLayout(QRect(0, 0, width, 0), true);
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlowLayout::setGeometry(const QRect &rect) {
|
||||||
|
QLayout::setGeometry(rect);
|
||||||
|
doLayout(rect, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize FlowLayout::sizeHint() const {
|
||||||
|
return minimumSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize FlowLayout::minimumSize() const {
|
||||||
|
QSize size;
|
||||||
|
for (const QLayoutItem *item: std::as_const(itemList))
|
||||||
|
size = size.expandedTo(item->minimumSize());
|
||||||
|
|
||||||
|
const QMargins margins = contentsMargins();
|
||||||
|
size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlowLayout::doLayout(const QRect &rect, bool testOnly) const {
|
||||||
|
int left, top, right, bottom;
|
||||||
|
getContentsMargins(&left, &top, &right, &bottom);
|
||||||
|
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
|
||||||
|
int x = effectiveRect.x();
|
||||||
|
int y = effectiveRect.y();
|
||||||
|
int lineHeight = 0;
|
||||||
|
for (QLayoutItem *item: std::as_const(itemList)) {
|
||||||
|
const QWidget *wid = item->widget();
|
||||||
|
int spaceX = horizontalSpacing();
|
||||||
|
if (spaceX == -1)
|
||||||
|
spaceX = wid->style()->layoutSpacing(
|
||||||
|
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
|
||||||
|
int spaceY = verticalSpacing();
|
||||||
|
if (spaceY == -1)
|
||||||
|
spaceY = wid->style()->layoutSpacing(
|
||||||
|
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
|
||||||
|
int nextX = x + item->sizeHint().width() + spaceX;
|
||||||
|
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
|
||||||
|
x = effectiveRect.x();
|
||||||
|
y = y + lineHeight + spaceY;
|
||||||
|
nextX = x + item->sizeHint().width() + spaceX;
|
||||||
|
lineHeight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!testOnly)
|
||||||
|
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
|
||||||
|
|
||||||
|
x = nextX;
|
||||||
|
lineHeight = qMax(lineHeight, item->sizeHint().height());
|
||||||
|
}
|
||||||
|
return y + lineHeight - rect.y() + bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlowLayout::addItem(QLayoutItem *item) {
|
||||||
|
itemList.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const {
|
||||||
|
QObject *parent = this->parent();
|
||||||
|
if (!parent) {
|
||||||
|
return -1;
|
||||||
|
} else if (parent->isWidgetType()) {
|
||||||
|
QWidget *pw = static_cast<QWidget *>(parent);
|
||||||
|
return pw->style()->pixelMetric(pm, nullptr, pw);
|
||||||
|
} else {
|
||||||
|
return static_cast<QLayout *>(parent)->spacing();
|
||||||
|
}
|
||||||
|
}
|
||||||
45
views/widget/FlowLayout.h
Normal file
45
views/widget/FlowLayout.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QLayout>
|
||||||
|
|
||||||
|
class FlowLayout : public QLayout {
|
||||||
|
public:
|
||||||
|
explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
|
||||||
|
|
||||||
|
explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
|
||||||
|
|
||||||
|
~FlowLayout();
|
||||||
|
|
||||||
|
void addItem(QLayoutItem *item) override;
|
||||||
|
|
||||||
|
int horizontalSpacing() const;
|
||||||
|
|
||||||
|
int verticalSpacing() const;
|
||||||
|
|
||||||
|
Qt::Orientations expandingDirections() const override;
|
||||||
|
|
||||||
|
bool hasHeightForWidth() const override;
|
||||||
|
|
||||||
|
int heightForWidth(int) const override;
|
||||||
|
|
||||||
|
int count() const override;
|
||||||
|
|
||||||
|
QLayoutItem *itemAt(int index) const override;
|
||||||
|
|
||||||
|
QSize minimumSize() const override;
|
||||||
|
|
||||||
|
void setGeometry(const QRect &rect) override;
|
||||||
|
|
||||||
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
|
QLayoutItem *takeAt(int index) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int doLayout(const QRect &rect, bool testOnly) const;
|
||||||
|
|
||||||
|
int smartSpacing(QStyle::PixelMetric pm) const;
|
||||||
|
|
||||||
|
QList<QLayoutItem *> itemList;
|
||||||
|
int m_hSpace;
|
||||||
|
int m_vSpace;
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user