IscanderChe
IscanderCheJuly 30, 2019, 3:06 a.m.

Simple Tracker project. Part 6: server. Server slots

We will consider slots that are directly related to the server in conjunction with the client. For now, let's focus on those slots that relate to project and task management and general application slots.

Let's start with connections. I moved them into a separate method void TrackerServer::createConnections() .

// trackerserver.cpp

// Метод создания подключений
void TrackerServer::createConnections()
{
    // Установка текущего проекта
    connect(projectView, &QListView::clicked, this, &TrackerServer::slotSetCurrentProject);
    connect(projectView, &QListView::activated, this, &TrackerServer::slotSetCurrentProject);

    // Подключение сигнала о смене статуса к обработчику
    connect(statusDelegate, &ComboBoxDelegate::commitData, this, &TrackerServer::slotChangeTaskStatus);

    // Создание новой задачи
    connect(newTaskButton, &QPushButton::clicked, this, &TrackerServer::slotNewTask);

    // Редактирование существующей задачи
    connect(editTaskButton, &QPushButton::clicked, this, &TrackerServer::slotEditTask);
    connect(taskView, &QTableView::doubleClicked, this, &TrackerServer::slotEditTask);

    // Удаление существующей задачи
    connect(deleteTaskButton, &QPushButton::clicked, this, &TrackerServer::slotDeleteTask);

    // Создание нового проекта
    connect(newProjectButton, &QPushButton::clicked, this, &TrackerServer::slotNewProject);

    // Архивация существующего проекта
    connect(archiveProjectButton, &QPushButton::clicked, this, &TrackerServer::slotArchiveProject);

    // Открытие архива проектов
    connect(openArchiveButton, &QPushButton::clicked, this, &TrackerServer::slotOpenArchive);

    // Открытие окна трекера
    connect(actionOpenWindow, &QAction::triggered, this, &TrackerServer::slotOpenTrackerWindow);

    // Открытие окна трекера по двойному клику на иконке в трее
    connect(trayIcon, &QSystemTrayIcon::activated, this, &TrackerServer::slotDoubleClickOpenTrackerWindow);

    // Завершение работы трекера
    connect(actionQuit, &QAction::triggered, qApp, &QApplication::quit);

    // Открытие окна настроек приложения
    connect(actionSettings, &QAction::triggered, this, &TrackerServer::slotOpenSettingsWindow);
}

To simplify working with project list items, we define an enumeration for Qt::DisplayRole and additional roles Qt::UserRole .

// trackerserver.h

enum ProjectProperty
{
    VisibleName = Qt::DisplayRole,
    Name = Qt::UserRole + 1,
    Id = Qt::UserRole + 2,
    Manual = Qt::UserRole + 3,
    Arch = Qt::UserRole + 4
};
// trackerserver.cpp

// Слот установки текущего проекта
void TrackerServer::slotSetCurrentProject(QModelIndex index)
{
    // Получаем элемент модели, соответствующий полученному индексу
    QStandardItem* item = projectListModel->itemFromIndex(index);
    // Получаем id текущего проекта
    projectCurrentId = item->data(ProjectProperty::Id).toInt();
    // Устанавливаем условие фильтрации таблицы задач по текущему id проекта
    taskFilterModel->setFilterFixedString(QString("%1").arg(projectCurrentId));

    // Обновляем представление таблицы задач
    updateTaskView();

    // Считаем количество строк в модели задач
    int rowNumber = taskFilterModel->rowCount();

    // Если количество строк больше нуля
    if(rowNumber > 0)
    {
        // Получаем индекс нулевого элемента модели задач
        int row = 0;
        QModelIndex index = taskFilterModel->index(row, CustomSortFilterProxyModel::E_COLUMN_NUMBER);
        // Устанавливаем в представлении текущий элемент с этим индексом
        taskView->setCurrentIndex(index);
        // Делаем доступными кнопки редактирования и удаления задач
        editTaskButton->setEnabled(true);
        deleteTaskButton->setEnabled(true);
    }
    else
    {
        // Иначе блокируем кнопки редактирования и удаления задач
        editTaskButton->setDisabled(true);
        deleteTaskButton->setDisabled(true);
    }

    // Делаем доступной кнопку создания новой задачи
    newTaskButton->setEnabled(true);

    // Проверяем, закрыты ли все задачи текущего проекта
    if(database->isClosedAllTasks(projectCurrentId))
        // Если да, то делаем доступной кнопку архивирования проекта
        archiveProjectButton->setEnabled(true);
    else
        // Если нет, то блокируем кнопку архивирования проекта
        archiveProjectButton->setDisabled(true);

    // Устанавливаем в виде текущим элемент с полученным индексом
    projectView->setCurrentIndex(index);
}

// Слот создания нового проекта
void TrackerServer::slotNewProject()
{
    // Объявляем диалог создания нового проекта
    QSharedPointer<NewProjectDialog> dialog(new NewProjectDialog());
    // Созадаём диалоговое окно
    if(dialog->exec() == QDialog::Accepted)
    {
        // Если в диалоге нажата кнопка "ОК",
        // получаем из диалога наименование проекта
        QString projectName = dialog->getProjectName();
        // Если наименование проекта не пустое
        if(!projectName.isEmpty())
        {
            // Добавляем проект со свойство "без контроля СКВ"
            database->insertNewProject(projectName, ProjectVCS::WithoutVCS);
            // Обновляем модель проектов
            projectTableModel->select();

            // Устанавливаем свойства элемента
            QString visibleProjectName = projectName;
            int row = projectTableModel->rowCount() - 1;
            int projectId =
                projectTableModel->record(row).value(idProjectCol).toInt();
            int projectManual = ProjectVCS::WithoutVCS;
            int projectArch = ProjectArchive::Active;

            visibleProjectName = "(M) " + projectName;

            QStandardItem* item = new QStandardItem();
            // Загружаем данные в элемент
            item->setData(visibleProjectName, ProjectProperty::VisibleName);
            item->setData(projectName, ProjectProperty::Name);
            item->setData(projectId, ProjectProperty::Id);
            item->setData(projectManual, ProjectProperty::Manual);
            item->setData(projectArch, ProjectProperty::Arch);

            // Загружаем элемент в модель представления проектов
            projectListModel->appendRow(item);

            // Устанавливаем текущий id проекта равный id нового проекта
            projectCurrentId = projectId;
            // Получаем индекс элемента нового проекта в модели представления
            int column = 0;
            QModelIndex currentProjectIndex = projectListModel->index(row, column);
            // Устанавливаем условие фильтрации таблицы задач по текущему id проекта
            taskFilterModel->setFilterFixedString(QString("%1").arg(projectCurrentId));
            // Устанвливаем текущий проект по индексу
            slotSetCurrentProject(currentProjectIndex);

            // Поскольку новый проект пустой, блокируем кнопки удаления и редактирования задач,
            // и делаем доступной кнопку создания новой задачи
            editTaskButton->setDisabled(true);
            deleteTaskButton->setDisabled(true);
            newTaskButton->setEnabled(true);
        }
    }
}

// Слот архивации проекта
void TrackerServer::slotArchiveProject()
{
    // Определяем индекс текущего проекта
    QModelIndex currentProjectIndex = projectView->currentIndex();
    int row = currentProjectIndex.row();

    // Скрываем проект в представлении
    projectView->setRowHidden(row, true);

    // Устанавливаем для элемента модели пресдтавления свойство архивации проекта
    QStandardItem* item = projectListModel->item(row);
    item->setData(ProjectArchive::Archive, ProjectProperty::Arch);
    projectListModel->setItem(row, item);

    // Получаем из элемента модели представления id проекта
    int projectId = item->data(ProjectProperty::Id).toInt();
    // Архивируем проект
    database->archiveProject(projectId);

    // Блокируем кнопку архивации проектов
    archiveProjectButton->setDisabled(true);

    // Определяем, остались ли видимые элементы в списке проектов
    int numberRows = projectListModel->rowCount();
    int numberRowsNotHidden = 0;
    int firstRowNotHidden = 0;

    for(int row = 0; row < numberRows; ++row)
    {
        if(!projectView->isRowHidden(row))
        {
            ++numberRowsNotHidden;
            firstRowNotHidden = row;
            break;
        }
    }

    if(numberRowsNotHidden > 0)
    {
        // Если видимые элементы остались, определем индекс первого
        QModelIndex index = projectListModel->index(firstRowNotHidden, 0);
        // Устанавливаем этот элемент текущим проектом
        slotSetCurrentProject(index);
    }
    else
    {
        // Если видимых элементов нет, обнуляем текущий id проекта
        projectCurrentId = 0;
        // Устанавливаем условие фильтрации таблицы задач по текущему id проекта
        taskFilterModel->setFilterFixedString(QString("%1").arg(projectCurrentId));
        // Обновляем представление таблицы задач
        updateTaskView();
    }

}

// Слот открытия архива
void TrackerServer::slotOpenArchive()
{
    // Объявляем диалог открытия архива
    QSharedPointer<OpenArchiveDialog> dialog(new OpenArchiveDialog(projectListModel));
    // Если подтверждено извлечение из архива проекта/проектов
    if(dialog->exec() == QDialog::Accepted)
    {
        // Получаем список id извлекаемых из архива проектов
        QList<int> projectIdList = dialog->getListProjectId();
        int lastId = projectIdList.last();
        // Извлекаем проекты из архива
        foreach(int id, projectIdList)
            database->extractProject(id);

        // Обновляем модель списка проектов
        updateProjectListModel();
        // Обновляем виджет списка проектов
        updateProjectView();

        int numberRows = projectListModel->rowCount();

        // Определяем индес последнего извлечённого проекта
        QModelIndex lastIndex;
        for(int row = 0; row < numberRows; ++row)
        {
            QStandardItem* item = projectListModel->item(row);
            int projectId = item->data(ProjectProperty::Id).toInt();
            if(projectId == lastId)
                lastIndex = projectListModel->indexFromItem(item);
        }

        // Устанавливаем текущим последний извлечённый проект
        slotSetCurrentProject(lastIndex);
    }
}

// Слот изменения состояния задачи
void TrackerServer::slotChangeTaskStatus(QWidget* /* editor */)
{
    // Обновляем модель SQL-таблицы задач
    taskSqlTableModel->select();

    if(database->isClosedAllTasks(projectCurrentId))
        // Если все задачи закрыты, делаем доступной кнопку архивации проектов
        archiveProjectButton->setEnabled(true);
    else
        // Если нет, блокируем кнопку архивации проектов
        archiveProjectButton->setDisabled(true);
}

// Слот создания новой задачи
void TrackerServer::slotNewTask()
{
    // Объявляем диалог создания / редактирования задачи
    QSharedPointer<NewEditTaskDialog> dialog(new NewEditTaskDialog());
    // Если создание новой задачи подтверждено
    if(dialog->exec() == QDialog::Accepted)
    {
        // Получаем из диалога тип и описание задачи
        QString type = dialog->getType();
        QString description = dialog->getDescription();
        // Если тип задачи и описание не пустые
        if(!type.isEmpty() && !description.isEmpty())
        {
            // Добавляем новую задачу
            database->insertNewTask(projectCurrentId, type, description);
            // Обновляем модель SQL-таблицы задач
            taskSqlTableModel->select();
            // Обновляем представление задач
            updateTaskView();
            // Определяем номер строки созданной задачи
            int row = taskFilterModel->rowCount() - 1;
            // Определяем индекс новой задачи в модели представления задач
            QModelIndex index = taskFilterModel->index(row, CustomSortFilterProxyModel::E_COLUMN_NUMBER);
            // Устанавливаем эту задачу текущей в представлении
            taskView->setCurrentIndex(index);
            // Делаем доступными кнопки редактирования и удаления задач
            editTaskButton->setEnabled(true);
            deleteTaskButton->setEnabled(true);
        }
    }
}

// Слот редактирования задачи
void TrackerServer::slotEditTask()
{
    // Объявляем диалог создания / редактирования задачи
    QSharedPointer<NewEditTaskDialog> dialog(new NewEditTaskDialog());

    // Извлекаем из текущего индекса задачи id, тип и описание задачи
    QModelIndex currentIndex = taskView->currentIndex();
    QModelIndex idIndex = taskFilterModel->index(currentIndex.row(), CustomSortFilterProxyModel::E_COLUMN_NUMBER);
    QModelIndex typeIndex = taskFilterModel->index(currentIndex.row(), CustomSortFilterProxyModel::E_COLUMN_TYPE);
    QModelIndex descriptionIndex = taskFilterModel->index(currentIndex.row(), CustomSortFilterProxyModel::E_COLUMN_DESCRIPTION);

    QString type = taskFilterModel->data(typeIndex, Qt::DisplayRole).toString();
    QString description = taskFilterModel->data(descriptionIndex, Qt::DisplayRole).toString();
    int id = taskFilterModel->data(idIndex, Qt::DisplayRole).toInt();

    // Передаём в диалог тип и описание задачи
    dialog->setType(type);
    dialog->setDescription(description);

    // Если редактирование задачи подтверждено
    if(dialog->exec() == QDialog::Accepted)
    {
        // Если тип или описание задачи отредактированы
        if(type != dialog->getType() || description != dialog->getDescription())
        {
            // Редактируем задачу в базе
            database->editTask(id, dialog->getType(), dialog->getDescription());
            // Обновляем модель SQL-таблицы задач
            taskSqlTableModel->select();
            // Обновляем представление задач
            updateTaskView();
            // Устанавливаем отредактированную задачу текущей в представлении
            taskView->setCurrentIndex(currentIndex);
        }
    }
}

// Слот удаления задачи
void TrackerServer::slotDeleteTask()
{
    // Объявляем диалог удаления задачи
    QSharedPointer<DeleteTaskDialog> dialog(new DeleteTaskDialog());

    // Извлекаем из индекса id задачи
    QModelIndex currentIndex = taskView->currentIndex();
    QModelIndex idIndex = taskFilterModel->index(currentIndex.row(), CustomSortFilterProxyModel::E_COLUMN_NUMBER);
    int id = taskFilterModel->data(idIndex, Qt::DisplayRole).toInt();

    // Если удаление задачи подтверждено
    if(dialog->exec() == QDialog::Accepted)
    {
        // Удаляем задачу из базы
        database->deleteTask(id);
        // Обновляем модель SQL-таблицы задач
        taskSqlTableModel->select();
        // Обновляем представление задач
        updateTaskView();
        // Определяем, остались ли видимые задачи
        int numberRows = taskFilterModel->rowCount();
        if(numberRows > 0)
        {
            // Если остались, делаем текущей первую
            int row = 0;
            QModelIndex index = taskFilterModel->index(row, CustomSortFilterProxyModel::E_COLUMN_NUMBER);
            taskView->setCurrentIndex(index);
        }
        else if(numberRows == 0)
        {
            // Если нет, блокируем кнопки редактирования и удаления задач
            editTaskButton->setDisabled(true);
            deleteTaskButton->setDisabled(true);
        }
    }
}

// Слот открытия окна трекера
void TrackerServer::slotOpenTrackerWindow()
{
    setVisible(true);
}

// Слот открытия окна трекера по двойному клику на иконке в трее

void TrackerServer::slotDoubleClickOpenTrackerWindow(QSystemTrayIcon::ActivationReason reason)
{
    if(reason == QSystemTrayIcon::DoubleClick)
        setVisible(true);
}

// Слот открытия окна настроек программы
void TrackerServer::slotOpenSettingsWindow()
{
    QSharedPointer<SettingsDialog> dialog(new SettingsDialog());
    dialog->exec();
}

Creating a project through the tracker is limited only to projects without VCS control. This is done to simplify the functionality of the tracker itself. Projects under SLE control are tracked by the tracker using the following code.

// trackerserver.cpp

// Метод инициализации проектов под СКВ
void TrackerServer::initProjectsWithVCS()
{
    // В переменную записываем путь к репозиториям из объекта settings класса QSettings
    QString pathToProjects = settings.value("PathToProjects", "").toString();
    if(!pathToProjects.isEmpty())
    {
        // Если путь не пустой, формируем из списка репозиториев список проектов под СКВ
        QDir dir(pathToProjects);
        QStringList projectFileList;

        foreach(QFileInfo item, dir.entryInfoList())
        {
            if(item.isDir())
            {
                QString dirPath = item.absoluteFilePath();
                QString dirName = item.fileName();
                // Критерием проекта под СКВ является наличие в репозитории в папке hooks
                // файла log.txt, куда хуки pre-commit и post-commit вносят информацию
                // о номере коммита и текст коммита с номером закрываемой задачи
                QString fileNamePath = dirPath + "/hooks/log.txt";
                QFile file(fileNamePath);
                if(file.exists())
                    projectFileList << dirName;
            }
        }

        if(!projectFileList.isEmpty())
        {
            // Если список проектов под СКВ не пустой, извлекаем из базы данных
            // список проектов и соответствующих им id проектов
            QStringList projectNameList;
            QList<int> projectIdList;

            projectTableModel->select();

            int numberRows = projectTableModel->rowCount();

            for(int row = 0; row < numberRows; ++row)
            {
                int projectId =
                    projectTableModel->record(row).value(idProjectCol).toInt();
                QString projectName =
                    projectTableModel->record(row).value(visibleNameProjectCol).toString();
                int manualFlag =
                    projectTableModel->record(row).value(manualPropertyCol).toInt();
                if(manualFlag == ProjectVCS::WithVCS)
                {
                    projectIdList << projectId;
                    projectNameList << projectName;
                }
            }

            // Формируем список новых проектов, отсутствующих в базе
            QStringList newProjectList;

            foreach(QString file, projectFileList)
            {
                int count = 0;
                foreach(QString project, projectNameList)
                {
                    if(file == project)
                        ++count;
                }
                if(count == 0)
                    newProjectList << file;
            }

            // Формируем список проектов, подлежащих автоматической архивации,
            // если репозитории этих проектов были удалены
            QStringList archiveProjectList;

            foreach(QString project, projectNameList)
            {
                int count = 1;
                foreach(QString file, projectFileList)
                {
                    if(project == file)
                        --count;
                }
                if(count > 0)
                    archiveProjectList << project;
            }

            // Вносим новые проекты по списку
            foreach(QString project, newProjectList)
                    database->insertNewProject(project, ProjectVCS::WithVCS);

            // Архивируем проекты по списку
            foreach(QString project, archiveProjectList)
            {
                for(int i = 0; i < projectNameList.size(); ++i)
                {
                    if(project == projectNameList.at(i))
                    {
                        database->archiveProject(projectIdList.at(i));
                        // Получае перечень задач для данного проекта
                        QList<int> taskIdList = database->getIdListForProject(projectIdList.at(i));
                        foreach(int taskId, taskIdList)
                        {
                            if(!database->isClosed(taskId))
                                // Если задача не закрыта, закрываем её с пометкой "forced"
                                database->closeTask(taskId, "forced");
                        }
                    }
                }
            }

            // Обновляем SQL-модель таблицы задач
            taskSqlTableModel->select();
        }
    }
}
We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

Evgenii Legotckoi
  • July 30, 2019, 4:24 a.m.

В общем-то достаточно написать так

// trackerserver.h

enum ProjectProperty
{
    VisibleName = Qt::DisplayRole,
    Name = Qt::UserRole + 1,
    Id,
    Manual,
    Arch
};

Все enum после Name будут и так увеличиваться на единицу.

Объявление переменной row не нужно

        int row = 0;
        QModelIndex index = taskFilterModel->index(row, CustomSortFilterProxyModel::E_COLUMN_NUMBER);
        // Устанавливаем в представлении текущий элемент с этим индексом
        taskView->setCurrentIndex(index);

Можно переписать так, читабельность от этого не страдает. 0 здесь не будет магической цифрой, поскольку здесь очевидно, что выбирается первая строка.

        taskView->setCurrentIndex(taskFilterModel->index(0, CustomSortFilterProxyModel::E_COLUMN_NUMBER));

foreach выполняет глубокое копирование , лучше переходите полноценно на современные стандарты C++. То есть вместо этого

        foreach(int id, projectIdList)
            database->extractProject(id);

Можно написать так

        for(int id : projectIdList)
            database->extractProject(id);

К тому же foreach - это макрос, а значит получаем работу препроцессора, которае в случае конструкций языка, например for, не происходит. Конечно есть случае, где foreach ещё оправдан, но их мало на самом деле.

Там где у вас QModelIndex берутся, также можно переписать в одну строку, как в предыдущем варианте. В конечном счёте и так ясно, что за это индексы по enum CustomSortFilterProxyModel::E_COLUMN_TYPE например.

Думаю, что вот это

int numberRows = taskFilterModel->rowCount();
        if(numberRows > 0)
        {
            // Если остались, делаем текущей первую
            int row = 0;
            QModelIndex index = taskFilterModel->index(row, CustomSortFilterProxyModel::E_COLUMN_NUMBER);
            taskView->setCurrentIndex(index);
        }
        else if(numberRows == 0)
        {
            // Если нет, блокируем кнопки редактирования и удаления задач
            editTaskButton->setDisabled(true);
            deleteTaskButton->setDisabled(true);
        }

Можно переписать так

// rowCount не должен возвращать -1 в принципе, поэтому второе условие просто лишнее
if (taskFilterModel->rowCount() > 0)
{
    // Одной строкой достаточно, и так ясно, что происходит благодаря CustomSortFilterProxyModel::E_COLUMN_NUMBER
    taskView->setCurrentIndex(taskFilterModel->index(0, CustomSortFilterProxyModel::E_COLUMN_NUMBER));
}
else
{
    editTaskButton->setDisabled(true);
    deleteTaskButton->setDisabled(true);
}

Вот это лишнее переусложнение

void TrackerServer::slotOpenSettingsWindow()
{
    QSharedPointer<SettingsDialog> dialog(new SettingsDialog());
    dialog->exec();
}

Просто создайте на стеке и тогда по окончанию работы диалог сам уничтожиться, не говоря уже о том, что в предыдущем варианте лишнее создание умного указателя с созданием объекта диалога и его копированием.

void TrackerServer::slotOpenSettingsWindow()
{
    SettingsDialog dialog;
    dialog.exec();
}

Вот это

QList<int> taskIdList = database->getIdListForProject(projectIdList.at(i));
foreach(int taskId, taskIdList)
{
    if(!database->isClosed(taskId))
        // Если задача не закрыта, закрываем её с пометкой "forced"
        database->closeTask(taskId, "forced");
}

Можно ак переписать

for (int taskId :  database->getIdListForProject(projectIdList.at(i)))
{
    if (!database->isClosed(taskId))
        database->closeTask(taskId, "forced");
}

P/S/ Вы можете в статье добавлять рекомендуемые статьи, например, список всех статей по Simple Tracker. Тогда пользователи смогу лучше ориентироваться по всем вашим статьям.

IscanderChe
  • July 30, 2019, 5:19 a.m.

Все enum после Name будут и так увеличиваться на единицу.

Это мне самому нужно было, чтобы при переписываниии с Qt::UserRole + n на enum не запутаться. Сам себе проблему придумал, поскольку сразу не ввёл enum.

Там где у вас QModelIndex берутся, также можно переписать в одну строку, как в предыдущем варианте.

Тоже пишу так для себя, поскольку ещё не до окнца освоился с QModelIndex.

lang-cpp for(int id : projectIdList)

Понял. Да, спасибо.

// rowCount не должен возвращать -1 в принципе, поэтому второе условие просто лишнее

Согласен.

Просто создайте на стеке и тогда по окончанию работы диалог сам уничтожиться, не говоря уже о том, что в предыдущем варианте лишнее создание умного указателя с созданием объекта диалога и его копированием.

Понял.

Comments

Only authorized users can post comments.
Please, Log in or Sign up
d
  • dsfs
  • April 26, 2024, 2:56 p.m.

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:80points,
  • Rating points4
d
  • dsfs
  • April 26, 2024, 2:45 p.m.

C++ - Test 002. Constants

  • Result:50points,
  • Rating points-4
d
  • dsfs
  • April 26, 2024, 2:35 p.m.

C++ - Test 001. The first program and data types

  • Result:73points,
  • Rating points1
Last comments
k
kmssrFeb. 9, 2024, 5:43 a.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 9:30 p.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 7:38 p.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 19, 2023, 8:01 a.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
G
GarApril 22, 2024, 3:46 p.m.
Clipboard Как скопировать окно целиком в clipb?
DA
Dr Gangil AcademicsApril 20, 2024, 5:45 p.m.
Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
a
a_vlasovApril 14, 2024, 4:41 p.m.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел ДорофеевApril 14, 2024, 12:35 p.m.
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrexApril 4, 2024, 2:47 p.m.
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

Follow us in social networks