#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); setWindowIcon(QIcon(":/images/mw-icon.png")); int width = frameGeometry().width(); int height = frameGeometry().height(); QScreen *screen = qApp->primaryScreen(); int screenWidth = screen->geometry().width(); int screenHeight = screen->geometry().height(); setGeometry((screenWidth/2)-(width/2), (screenHeight/2)-(height/2), width, height); QStringList filterItems = { "Все устройства", "Типы устройств", "Помещения", "Отделы", "Производители", "Модели устройств" }; ui->comboBoxFilters->addItems(filterItems); QStringList sortItems = { "Сначала новые", "Сначала старые", "Сначала дешевые", "Сначала дорогие", "Сначала с лайком" }; ui->comboBoxSort->addItems(sortItems); ui->tableWidget->verticalHeader()->setVisible(false); ui->tableWidget->setColumnCount(2); QStringList headers = {"ID", "Название"}; ui->tableWidget->setHorizontalHeaderLabels(headers); QHeaderView *header = ui->tableWidget->horizontalHeader(); QFont headerFont = header->font(); headerFont.setBold(true); header->setFont(headerFont); header->setSectionResizeMode(QHeaderView::Stretch); ui->listWidgetDevices->setSpacing(10); QLineEdit *searchEdit = ui->lineEditSearch; QPushButton *searchButton = new QPushButton(this); ButtonHoverWatcher *watcher = new ButtonHoverWatcher(this); searchButton->installEventFilter(watcher); searchButton->setCursor(Qt::PointingHandCursor); searchButton->setStyleSheet("background: transparent; border: none;"); searchButton->setIcon(QIcon(":/images/search.png")); searchButton->setFixedSize(22, 22); QMargins margins = searchEdit->textMargins(); searchEdit->setTextMargins(margins.left(), margins.top(), searchButton->width(), margins.bottom()); searchEdit->setPlaceholderText(QStringLiteral("Поиск устройств по модели, типу или серийному номеру...")); searchEdit->setMaxLength(200); QHBoxLayout *searchLayout = new QHBoxLayout(); searchLayout->addStretch(); searchLayout->addWidget(searchButton); searchLayout->setSpacing(0); searchLayout->setContentsMargins(3, 2, 3, 2); searchEdit->setLayout(searchLayout); QButtonGroup *buttonGroup = new QButtonGroup(this); buttonGroup->addButton(ui->radioButtonWorking); buttonGroup->addButton(ui->radioButtonNotWorking); buttonGroup->addButton(ui->radioButtonDisregard); ui->radioButtonWorking->setChecked(true); ui->checkBoxIsWorking->setAttribute(Qt::WA_TransparentForMouseEvents); ui->checkBoxIsWorking->setFocusPolicy(Qt::NoFocus); ui->pushButtonDisregardPrice->setToolTip("Цена не учитывается при том условии, если оба значения из диапазона равны нулю"); QFile *styleFile = new QFile(":/styles.qss"); if (styleFile->open(QFile::ReadOnly)) { QTextStream ts(styleFile); QString style = ts.readAll(); qApp->setStyleSheet(style); } connect(ui->comboBoxFilters, SIGNAL(activated(int)), this, SLOT(updateTableWidget(int))); connect(ui->comboBoxSort, SIGNAL(activated(int)), this, SLOT(changeSortOrder())); connect(ui->tableWidget->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(updateListWidget())); connect(ui->listWidgetDevices, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(showItemInfo(QListWidgetItem*))); connect(ui->pushButtonBack, SIGNAL(clicked()), this, SLOT(pushButtonBackClicked())); connect(ui->pushButtonClear, SIGNAL(clicked()), this, SLOT(pushButtonClearClicked())); connect(searchEdit, SIGNAL(returnPressed()), this, SLOT(pushButtonSearchClicked())); connect(searchButton, SIGNAL(clicked()), this, SLOT(pushButtonSearchClicked())); connect(ui->pushButtonDisregardPrice, SIGNAL(clicked()), this, SLOT(pushButtonDisregardPriceClicked())); connect(ui->pushButtonApplyFilters, SIGNAL(clicked()), this, SLOT(pushButtonApplyFiltersClicked())); connect(ui->pushButtonCancelFilters, SIGNAL(clicked()), this, SLOT(pushButtonCancelFiltersClicked())); connect(ui->pushButtonDefault, SIGNAL(clicked()), this, SLOT(pushButtonDefaultClicked())); connect(ui->pushButtonStructure, SIGNAL(clicked()), this, SLOT(pushButtonStructureClicked())); connect(ui->pushButtonCharacteristics, SIGNAL(clicked()), this, SLOT(pushButtonCharacteristicsClicked())); returnToDeviceList(); } MainWindow::~MainWindow() { delete ui; } void MainWindow::updateTableWidget(int index) { returnToDeviceList(); ui->tableWidget->clearContents(); ui->tableWidget->setRowCount(0); switch (index) { case 0: // Все устройства clearDevicesOutputSettings(); updateDevices(-1); break; case 1: // Типы устройств _client->getEntities("/api/devicetypes"); break; case 2: // Помещения _client->getEntities("/api/locations"); break; case 3: // Отделы _client->getEntities("/api/departments"); break; case 4: // Производители _client->getEntities("/api/manufacturers"); break; case 5: // Модели устройств _client->getEntities("/api/devicemodels"); break; default: break; } } void MainWindow::updateListWidget() { returnToDeviceList(); int selectedElementId = getSelectedElementId(); if (selectedElementId == -1) return; clearDevicesOutputSettings(); updateDevices(selectedElementId); } void MainWindow::changeSortOrder() { updateDevices(getSelectedElementId()); } void MainWindow::updateDevices(int entityId) { _client->getFilteredDevices( _filterParams.isWorking(), _filterParams.priceFrom(), _filterParams.priceTo(), _filterParams.apllyFilters(), _filterParams.disregardState(), entityId, ui->comboBoxFilters->currentIndex(), _searchInfo, ui->comboBoxSort->currentText() ); } int MainWindow::getSelectedElementId() { QModelIndexList selectedIndexes = ui->tableWidget->selectionModel()->selectedRows(); if (selectedIndexes.isEmpty()) { return -1; } int rowIndex = selectedIndexes.first().row(); return ui->tableWidget->item(rowIndex, 0)->text().toInt(); } template void MainWindow::fillTable(const QMap &map) { int row = 0; for (auto &item : map) { ui->tableWidget->insertRow(row); QTableWidgetItem *idItem = new QTableWidgetItem(QString::number(item.id())); QTableWidgetItem *nameItem = new QTableWidgetItem(item.name()); idItem->setFlags(idItem->flags() & ~Qt::ItemIsEditable); nameItem->setFlags(nameItem->flags() & ~Qt::ItemIsEditable); ui->tableWidget->setItem(row, 0, idItem); ui->tableWidget->setItem(row, 1, nameItem); idItem->setTextAlignment(Qt::AlignCenter); nameItem->setTextAlignment(Qt::AlignCenter); row++; } } void MainWindow::fillListWidget(const QList &devices) { ui->listWidgetDevices->clear(); if (devices.isEmpty()) { addMessageEmptyToList(); } else { for (const auto &device : devices) { addDeviceToList(device); } } } void MainWindow::addMessageEmptyToList() { QListWidgetItem *item = new QListWidgetItem(ui->listWidgetDevices); ui->listWidgetDevices->addItem(item); QWidget *cardWidget = createMessageEmptyCard(); item->setSizeHint(cardWidget->minimumSizeHint()); item->setFlags(item->flags() & ~Qt::ItemIsEnabled); ui->listWidgetDevices->setItemWidget(item, cardWidget); } void MainWindow::addDeviceToList(const Device &device) { QListWidgetItem *item = new QListWidgetItem(ui->listWidgetDevices); ui->listWidgetDevices->addItem(item); QWidget *cardWidget = createDeviceCard(device); item->setSizeHint(cardWidget->minimumSizeHint()); ui->listWidgetDevices->setItemWidget(item, cardWidget); item->setData(Qt::UserRole, device.id()); } QWidget *MainWindow::createMessageEmptyCard() { QWidget *cardWidget = new QWidget(ui->listWidgetDevices); QVBoxLayout *cardLayout = new QVBoxLayout(cardWidget); QLabel *titleLabel = new QLabel("По заданным параметрам ничего не найдено", cardWidget); QFont titleFont("Arial", 17, QFont::Bold); titleLabel->setFont(titleFont); cardLayout->addWidget(titleLabel, 0, Qt::AlignCenter); cardLayout->setSpacing(12); QLabel *imageLabel = new QLabel(cardWidget); QPixmap pixmap(":/images/question.png"); imageLabel->setPixmap(pixmap.scaled(256, 256, Qt::KeepAspectRatio)); cardLayout->addWidget(imageLabel, 0, Qt::AlignCenter); return cardWidget; } QWidget *MainWindow::createDeviceCard(const Device &device) { QWidget *cardWidget = new QWidget(ui->listWidgetDevices); QVBoxLayout *cardLayout = new QVBoxLayout(cardWidget); QFrame *topBorder = new QFrame(cardWidget); topBorder->setFrameShape(QFrame::HLine); topBorder->setFrameShadow(QFrame::Sunken); cardLayout->addWidget(topBorder); QHBoxLayout *iconTextlayout = new QHBoxLayout(); QLabel *imageLabel = new QLabel(cardWidget); QString imagePath = _deviceTypeImages[device.deviceModel().idType()]; QPixmap pixmap(imagePath); imageLabel->setPixmap(pixmap.scaled(100, 100, Qt::KeepAspectRatio)); iconTextlayout->addWidget(imageLabel); QVBoxLayout *textLayout = new QVBoxLayout(); QLabel *serialNumberLabel = new QLabel(device.serialNumber(), cardWidget); QFont serialNumberFont("Arial", 14, QFont::Bold); serialNumberLabel->setFont(serialNumberFont); textLayout->addWidget(serialNumberLabel); QFont generalFont("Arial", 11); QString typeName = normalizeTypeName(device.deviceModel().nameType()); QLabel *typeNameLabel = new QLabel(typeName + ": " + device.deviceModel().name(), cardWidget); typeNameLabel->setFont(generalFont); textLayout->addWidget(typeNameLabel); QLabel *statusLabel = new QLabel(device.isWorking() ? "Работает" : "Сломано", cardWidget); statusLabel->setFont(generalFont); textLayout->addWidget(statusLabel); QHBoxLayout *priceLikeLayout = new QHBoxLayout(); priceLikeLayout->setSpacing(100); QLabel *priceLabel = new QLabel(QString::number(device.price()) + " ₽", cardWidget); priceLabel->setFont(generalFont); priceLabel->setFixedWidth(100); priceLikeLayout->addWidget(priceLabel); priceLikeLayout->addSpacerItem(new QSpacerItem(40, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); QCheckBox *likeCheckBox = new QCheckBox("Нравится", cardWidget); likeCheckBox->setChecked(device.isLiked()); likeCheckBox->setFont(generalFont); likeCheckBox->setStyleSheet(likeCheckBox->isChecked() ? "color: green;" : "color: black;"); connect(likeCheckBox, &QCheckBox::toggled, this, [this, device, likeCheckBox](bool checked) { Device updatedDevice = device; updatedDevice.setIsLiked(checked); _client->updateDevice(updatedDevice.id(), updatedDevice); connect(_client, &ApiClient::deviceUpdated, this, [this, checked, device, likeCheckBox](bool success) { disconnect(_client, &ApiClient::deviceUpdated, this, nullptr); if (success) { QString message = checked ? "Устройство добавлено в избранное." : "Устройство удалено из избранного."; QMessageBox::information(this, "Обновление", message); Device& deviceRef = _mapDevices[device.id()]; deviceRef.setIsLiked(checked); likeCheckBox->setStyleSheet(checked ? "color: green;" : "color: black;"); } else { QMessageBox::critical(this, "Ошибка", "Не удалось изменить состояние устройства."); } }); }); priceLikeLayout->addWidget(likeCheckBox); textLayout->addLayout(priceLikeLayout); iconTextlayout->addSpacerItem(new QSpacerItem(40, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); iconTextlayout->addLayout(textLayout); cardLayout->addLayout(iconTextlayout); QFrame *bottomBorder = new QFrame(cardWidget); bottomBorder->setFrameShape(QFrame::HLine); bottomBorder->setFrameShadow(QFrame::Sunken); cardLayout->addWidget(bottomBorder); cardLayout->setContentsMargins(2, 0, 2, 0); return cardWidget; } QString MainWindow::normalizeTypeName(const QString &name) { QString typeName = name.left(name.length() - 1); if (typeName == "Персональные компьютер") typeName = "Персональный компьютер"; return typeName; } void MainWindow::showItemInfo(QListWidgetItem *item) { if (!item) return; ui->listWidgetDevices->scrollToItem(item, QAbstractItemView::PositionAtCenter); int deviceId = item->data(Qt::UserRole).toInt(); Device device = _mapDevices[deviceId]; _idCard = deviceId; ui->lineEditId->setText(QString::number(device.id())); ui->lineEditSerialNum->setText(device.serialNumber()); ui->lineEditPurchaseDate->setText(device.purchaseDate().toString("dd.MM.yyyy HH:mm:ss")); ui->lineEditPrice->setText(QString::number(device.price(), 'f', 2)); ui->lineEditWarranty->setText(device.warrantyExpireDate().toString("dd.MM.yyyy HH:mm:ss")); ui->lineEditLocation->setText(device.nameLocation()); ui->lineEditEmployee->setText(device.nameEmployee()); ui->lineEditDepartment->setText(device.nameDepartment()); ui->lineEditModel->setText(device.deviceModel().name()); ui->lineEditType->setText(device.deviceModel().nameType()); ui->lineEditManuf->setText(device.deviceModel().nameManuf()); ui->textEditFurtherInformation->setPlainText(device.furtherInformation()); ui->checkBoxIsWorking->setChecked(device.isWorking()); ui->stackedWidget->setCurrentIndex(1); } void MainWindow::returnToDeviceList() { _idCard = 0; ui->stackedWidget->setCurrentIndex(0); } void MainWindow::showTableWithStructureElements(const QList &elements) { QDialog* dialog = new QDialog(this); dialog->setWindowTitle("Элементы структуры устройства"); dialog->resize(1100, 600); QTableWidget* tableWidget = new QTableWidget(dialog); tableWidget->setColumnCount(5); QFont font("Arial", 12); tableWidget->setFont(font); QStringList headers = {"Название модели", "Производитель", "Описание элемента", "Тип элемента", "Количество"}; tableWidget->setHorizontalHeaderLabels(headers); QFont headerFont("Arial", 12, QFont::Bold); for (int i = 0; i < tableWidget->columnCount(); i++) { tableWidget->horizontalHeaderItem(i)->setFont(headerFont); } tableWidget->setRowCount(elements.size()); for (int row = 0; row < elements.size(); row++) { const DeviceStructureElement& element = elements[row]; tableWidget->setItem(row, 0, new QTableWidgetItem(element.nameModel())); tableWidget->setItem(row, 1, new QTableWidgetItem(element.nameManuf())); tableWidget->setItem(row, 2, new QTableWidgetItem(element.description())); tableWidget->setItem(row, 3, new QTableWidgetItem(element.nameType())); tableWidget->setItem(row, 4, new QTableWidgetItem(QString::number(element.count()))); for (int col = 0; col < tableWidget->columnCount(); col++) { tableWidget->item(row, col)->setFlags(tableWidget->item(row, col)->flags() & ~Qt::ItemIsEditable); } } connect(tableWidget, &QTableWidget::cellClicked, [tableWidget](int row, int col) { QString text = tableWidget->item(row, col)->text(); QToolTip::showText(QCursor::pos(), text); }); tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); tableWidget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); tableWidget->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Interactive); tableWidget->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch); tableWidget->horizontalHeader()->setSectionResizeMode(4, QHeaderView::ResizeToContents); tableWidget->setColumnWidth(2, tableWidget->columnWidth(0) * 2); tableWidget->setFocusPolicy(Qt::NoFocus); QVBoxLayout* layout = new QVBoxLayout(dialog); layout->addWidget(tableWidget); dialog->setLayout(layout); dialog->exec(); } void MainWindow::showTableWithDeviceModelCharacteristics(const DeviceModel &model) { QDialog* dialog = new QDialog(this); dialog->setWindowTitle("Характеристики устройства"); dialog->setFixedSize(1200, 75); QTableWidget* tableWidget = new QTableWidget(dialog); tableWidget->setColumnCount(6); QFont font("Arial", 11); tableWidget->setFont(font); QStringList headers = {"Эффективность работы", "Надежность", "Энергоэффективность", "Удобство использования", "Долговечность", "Эстетические качества"}; tableWidget->setHorizontalHeaderLabels(headers); QFont headerFont("Arial", 11, QFont::Bold); for (int i = 0; i < tableWidget->columnCount(); i++) { tableWidget->horizontalHeaderItem(i)->setFont(headerFont); } tableWidget->setRowCount(1); tableWidget->setItem(0, 0, new QTableWidgetItem(QString::number(model.workEfficiency()))); tableWidget->setItem(0, 1, new QTableWidgetItem(QString::number(model.reliability()))); tableWidget->setItem(0, 2, new QTableWidgetItem(QString::number(model.energyEfficiency()))); tableWidget->setItem(0, 3, new QTableWidgetItem(QString::number(model.userFriendliness()))); tableWidget->setItem(0, 4, new QTableWidgetItem(QString::number(model.durability()))); tableWidget->setItem(0, 5, new QTableWidgetItem(QString::number(model.aestheticQualities()))); for (int col = 0; col < tableWidget->columnCount(); col++) { tableWidget->item(0, col)->setFlags(tableWidget->item(0, col)->flags() & ~Qt::ItemIsEditable); tableWidget->item(0, col)->setTextAlignment(Qt::AlignCenter); tableWidget->horizontalHeader()->setSectionResizeMode(col, QHeaderView::Stretch); } tableWidget->verticalHeader()->setVisible(false); tableWidget->setFocusPolicy(Qt::NoFocus); QVBoxLayout* layout = new QVBoxLayout(dialog); layout->addWidget(tableWidget); dialog->setLayout(layout); dialog->exec(); } void MainWindow::pushButtonBackClicked() { returnToDeviceList(); } void MainWindow::pushButtonClearClicked() { ui->lineEditSearch->setText(""); } void MainWindow::pushButtonSearchClicked() { _searchInfo = ui->lineEditSearch->text(); if (_searchInfo.isEmpty()) { QMessageBox::warning(this, "Ошибка", "Пожалуйста, введите текст для поиска."); return; } ui->comboBoxSort->setCurrentIndex(0); clearFilters(); updateDevices(getSelectedElementId()); } void MainWindow::pushButtonDisregardPriceClicked() { clearSpinBoxes(); } void MainWindow::pushButtonApplyFiltersClicked() { double priceFrom = ui->doubleSpinBoxFrom->value(); double priceTo = ui->doubleSpinBoxTo->value(); if (priceFrom > priceTo) { QMessageBox::warning(this, "Ошибка", "Начальное значение диапазона не может быть больше конечного значения."); clearSpinBoxes(); return; } _filterParams.setApllyFilters(true); _filterParams.setDisregardState(ui->radioButtonDisregard->isChecked()); _filterParams.setIsWorking(ui->radioButtonWorking->isChecked()); if (priceFrom == 0.00 && priceTo == 0.00) { _filterParams.setPriceFrom(0.00); _filterParams.setPriceTo(std::numeric_limits::max()); } else { _filterParams.setPriceFrom(priceFrom); _filterParams.setPriceTo(priceTo); } updateDevices(getSelectedElementId()); } void MainWindow::pushButtonCancelFiltersClicked() { clearFilters(); updateDevices(getSelectedElementId()); } void MainWindow::pushButtonStructureClicked() { showTableWithStructureElements(_mapDevices[_idCard].deviceModel().structureElements()); } void MainWindow::pushButtonCharacteristicsClicked() { showTableWithDeviceModelCharacteristics(_mapDevices[_idCard].deviceModel()); } void MainWindow::pushButtonDefaultClicked() { clearDevicesOutputSettings(); updateDevices(getSelectedElementId()); } void MainWindow::clearDevicesOutputSettings() { ui->comboBoxSort->setCurrentIndex(0); ui->lineEditSearch->setText(""); _searchInfo = ""; clearFilters(); } void MainWindow::clearFilters() { ui->radioButtonWorking->setChecked(true); clearSpinBoxes(); _filterParams.setIsWorking(true); _filterParams.setPriceFrom(-1); _filterParams.setPriceTo(-1); _filterParams.setDisregardState(false); _filterParams.setApllyFilters(false); } void MainWindow::clearSpinBoxes() { ui->doubleSpinBoxFrom->setValue(0.00); ui->doubleSpinBoxTo->setValue(0.00); } void MainWindow::setClient(ApiClient *newClient) { _client = newClient; connect(_client, &ApiClient::devicesReceived, this, &MainWindow::onDevicesReceived); connect(_client, &ApiClient::departmentsReceived, this, &MainWindow::onDepartmentsReceived); connect(_client, &ApiClient::deviceModelsReceived, this, &MainWindow::onDeviceModelsReceived); connect(_client, &ApiClient::deviceTypesReceived, this, &MainWindow::onDeviceTypesReceived); connect(_client, &ApiClient::locationsReceived, this, &MainWindow::onLocationsReceived); connect(_client, &ApiClient::manufacturersReceived, this, &MainWindow::onManufacturersReceived); _client->getEntities("/api/devicetypes"); updateTableWidget(0); } void MainWindow::onDevicesReceived(const QList &devices) { _mapDevices = getDevicesMap(devices); fillListWidget(devices); } void MainWindow::onDepartmentsReceived(const QMap &departments) { fillTable(departments); } void MainWindow::onDeviceModelsReceived(const QMap &deviceModels) { fillTable(deviceModels); } void MainWindow::onDeviceTypesReceived(const QMap &deviceTypes) { if (_initialTypesLoad) { _initialTypesLoad = false; } else { fillTable(deviceTypes); } for (const DeviceType &type : deviceTypes) { QString imagePath = QString(":/images/device-type-%1.png").arg(type.name()); if (QFile::exists(imagePath)) { _deviceTypeImages[type.id()] = imagePath; } else { _deviceTypeImages[type.id()] = ":/images/placeholder.png"; } } } void MainWindow::onManufacturersReceived(const QMap &manufacturers) { fillTable(manufacturers); } void MainWindow::onLocationsReceived(const QMap &locations) { fillTable(locations); } QMap MainWindow::getDevicesMap(const QList &devices) { QMap devicesMap; for (const Device &device : devices) { devicesMap.insert(device.id(), device); } return devicesMap; }