Evgenii Legotckoi
Evgenii Legotckoi9 октября 2018 г. 14:16

Qt/C++ - Урок 083. Создание динамической библиотеки и подключение её в другой проект

На форуме возник вопрос, как создать динамическую библиотеку и правильно её подключить в сторонний проект. Периодически такие вопросы возникают, поэтому рассмотрим один вариант создание динамической библиотеки dll для Windows с использованием визардов стандартных в Qt Creator.

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

Создадим два проекта:

  1. QuiLib - это будет внешняя динамическая библиотека, которая будет содержать одно диалоговое окно. Данное диалоговое окно будет открываться в основном проекте.
  2. WithDynamicLibrary - проект, который будет использоваться как раз для подключения данной динамической библиотеки.

Шаг 1

Выберем в меню Qt Creator создание проекта и выберем тип нашего проекта. Это будет библиотека на C++.

Шаг 2

Пропишем название проекта и место его размещения

Шаг 3

Выберем комплекты для сборки проекта.

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

Я собираю данную библиотеку компилятором MSVC2017.

Шаг 4

Выбираем необходимые модули. Для нашей библиотеки хватит базового функционала.

Шаг 5

Дадим название классу библиотеки, который будет в нём использоваться. Название в данном случае будет такое же, как и название самой библиотеки. Но можно и поменять. Это не принципиально.

Шаг 6

Используете систему контроля версий? Так добавьте проект под контроль версий. Если нет, то ничего не делайте. И просто завершите создание библиотеки.

Шаг 7

Посмотрим на файлы проекта и немного модифицируем их.

Структура проекта

QuiLib.pro

В данном файле имеется информация о том, что это именно библиотека. Вот в данной строчке.

TEMPLATE = lib

Вот полный код pro-файла

#-------------------------------------------------
#
# Project created by QtCreator 2018-10-09T19:33:33
#
#-------------------------------------------------

QT       += widgets

TARGET = QuiLib
TEMPLATE = lib

DEFINES += QUILIB_LIBRARY

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        QuiLib.cpp

HEADERS += \
        QuiLib.h \
        quilib_global.h 

unix {
    target.path = /usr/lib
    INSTALLS += target
}

quilib_global.h

Хедер для определения дефайнов экспорта в библиотеке. Классы, которые будут помечены дефайном экспорта, будут доступны для использования вне библиотеки.

#ifndef QUILIB_GLOBAL_H
#define QUILIB_GLOBAL_H

#include <QtCore/qglobal.h>

#if defined(QUILIB_LIBRARY)
#  define QUILIBSHARED_EXPORT Q_DECL_EXPORT
#else
#  define QUILIBSHARED_EXPORT Q_DECL_IMPORT
#endif

#endif // QUILIB_GLOBAL_H

QuiLib.h

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

#ifndef QUILIB_H
#define QUILIB_H

#include "quilib_global.h"

#include <QDialog>

class QUILIBSHARED_EXPORT QuiLib : public QDialog
{

public:
    explicit QuiLib(QWidget* parent = nullptr);
};

#endif // QUILIB_H

QuiLib.cpp

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

#include "QuiLib.h"

#include <QLabel>
#include <QGridLayout>


QuiLib::QuiLib(QWidget* parent) : QDialog(parent)
{
    QGridLayout* gridLayout = new QGridLayout(this);
    setLayout(gridLayout);
    gridLayout->addWidget(new QLabel("Hello world from dynamic library", this));
}

Шаг 8

Скомпилируем проект в Debug и Release версиях.

Вот например, что будет в каталоге release сборки библиотеки. Из тех файлов нам понадобятся только файлы QuiLib.dll и QuiLib.lib. Помимо этих файлов нужны ещё будут заголовочный файлы из самого проекта, но об этом немного позднее.

Шаг 9

Создание проекта, который будет использовать эту динамическую библиотеку. Процесс создания будет стандартный, через визард в Qt Creator. Нужно будет выбрать Приложение на Qt.

Добавить название проекта и место его размещения

Шаг 10

Указать комплект сборки.

Шаг 11

Введём название класса главного окна приложения, а также укажем от какого класса наследоваться. Я выбрал QWidget.

Шаг 12

Снова выбор системы контроля версий и завершение процесса создания проекта.

Шаг 13

Структура проекта

В каталоге данного проекта создадим каталог QuiLib , в который поместим каталоги debug, release, include.

В данных каталогах будут содержаться скомпилированные библиотеки QuiLib.dll и QuiLib.lib соответственно отладочной версии и выпускаемой версии. В каталоге include будут заголовочные файлы QuiLib.h и quilib_global.h.

То есть мы представили ту ситуацию, в которой мы кому-то передали скомпилированную библиотеку, чтобы он мог её подключить и использовать.

Шаг 14

Добавляем библиотеку в проект с помощью визарда. Можно конечно и вручную всё прописать, но если сомневаетесь в своих силах, а это так и есть, иначе не читали бы эту статью, то используем визард.

Шаг 15

Мы знаем, что библиотека внешняя

Шаг 16

А также, что будем использовать её только для Windows. Здесь настроено, что файлы Debug и Release версий находятся в разных каталогах без всяких префиксов отладочных библиотек. Я их просто не настраивал. Достаточно указать одну из .lib * библиотек либо в debug, либо release каталоге. Путь к другой версии будет добавлен автоматически. Также нужно указать каталог, в котором располагаются заголовочные файлы. Традиционно это include** каталог.

Шаг 17

Завершаем добавление

Шаг 18

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

Так что теперь глянем в pro файл нашего проекта, который будет использовать динамическую библиотеку.

WithDynamicLibrary.pro

#-------------------------------------------------
#
# Project created by QtCreator 2018-10-09T19:45:20
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = WithDynamicLibrary
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

CONFIG += c++11

SOURCES += \
        main.cpp \
        Widget.cpp

HEADERS += \
        Widget.h

FORMS += \
        Widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

win32:CONFIG(release, debug|release): LIBS += -L$$PWD/QuiLib/release/ -lQuiLib
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/QuiLib/debug/ -lQuiLib

INCLUDEPATH += $$PWD/GuiLib/include
DEPENDPATH += $$PWD/GuiLib/include

Это самые последние строки в данном файле.

Widget.ui

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

Widget.h

Пропишем слот, для обработки клика кнопки.

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void onPushButtonClicked(); // Слот для обработки клика по кнопке

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

Widget.cpp

А теперь обработаем клик кнопки и вызовем диалоговое окно из внешней библиотеки.

#include "Widget.h"
#include "ui_Widget.h"

#include <QPushButton>

// Подключаем заголовочный файл библиотеки
#include <QuiLib/include/QuiLib.h>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    // Подключаем слот к сигналу от кнопки
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::onPushButtonClicked);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::onPushButtonClicked()
{
    // Вызываем диалоговое окно
    QuiLib libWidget(this);
    libWidget.exec();
}

Заметьте, что вызывать диалоговое окно нужно обязательно методом exec() чтобы запустилась внутренняя петля диалога, которая будет ожидать событий. Иначе диалог сразу закроется,  поскольку слот отработает, а диалог в данном случае создан на стеке метода и по завершению метода диалог уничтожится. А метод exec() завершится только тогда, когда произойдёт соответствующее событие, которое закроет диалог.

Результат

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

nayk1982
  • 9 октября 2018 г. 20:49

Хороший урок, все подробно расписано. Такой вопрос: версия Qt для дин.библиотеки не обязательно должна совпадать с версией Qt проекта, который эту библиотеку использует?

Evgenii Legotckoi
  • 10 октября 2018 г. 1:59

Спасибо ))

Из того, что я читал в документации, следует, что библиотеки Qt бинарно совместимы по минорным версиям. То есть, если проект работал с Qt 5.6, то можно поменять библиотеки на Qt 5.7 и по прежнему всё будет работать. На практике, конечно, не всегда всё гладко проходит. То есть по идее, если динамическая библиотека использует Qt 5.6, а подключили её в проект с Qt 5.7, то должно работать. Но опять же оговорюсь, на практике может выйти иначе, особенно, если динамическая библиотека использовала Qt 5.7, а подключили проект на Qt 5.6. Как минимум мождете оказаться, что в Qt 5.6 в каком-то классе отсутствуют некоторые методы.

То есть теоретически возможно, практически, как карта ляжет.



Docent
  • 10 октября 2018 г. 3:21

Вот какой вопрос возник: для запуска программы вне Qt приходится тащить с ехе'шником кучу dll. А для использования созданной dll не придется ли тащить с собой всё те же Qt5Core.dll, Qt5Gui.dll, Qt5Widgets.dll...? Особенно если дальнейшее использование созданной dll планируется без участия Qt

Evgenii Legotckoi
  • 10 октября 2018 г. 3:25

Погодите. Если речь идёт о библиотеке, которая использует Qt, от естественно, что ей понадобятся все те модули, от которых зависит бибилотека. Например в данном примере используются модули Qt5Core, Qt5Gui, Qt5Widgets, соответсвенно их тоже придётся тащить с собой. Если же вы создаёте библиотеку без участия Qt, то и модули Qt не будут нужны.

Вы не можете запланировать использование библиотеки без Qt, если она использует модули Qt, но если вы отказываетесь от использования Qt в библиотеке, то тогда получаете возможность не тащить все выше перечисленные модули, поскольку библиотека от них не зависит тогда.

o
  • 10 октября 2018 г. 3:28

Круто было бы прочитать про приложение с подключаемыми плагинами.

Evgenii Legotckoi
  • 10 октября 2018 г. 3:31

Типо как в Qt Creator?

Самому бы интересно было о таком почитать. В данный момент я бы мог написать только о написании плагинов для Qt Designer. С этим есть некоторый опыт.

o
  • 10 октября 2018 г. 4:46

ну типа того, создание программы, функционал которой можно расширять плагинами, и, в перспективе, создание API.

o
  • 10 октября 2018 г. 4:48

О плагинах к QtCreator в целом, тоже интересно.

Evgenii Legotckoi
  • 10 октября 2018 г. 4:50

Если и начинать писать о плагинах, то нужно тогда с Qt Creator начинать, там наверняка будет одинаковый принцип, но по Qt Creator хотя бы информация есть.

o
  • 10 октября 2018 г. 8:16

наверняка, так и есть)

B
  • 20 октября 2018 г. 11:51

В принципе у меня есть опыт реализации плагинов, могу что-нибудь накропать как будет время

Evgenii Legotckoi
  • 21 октября 2018 г. 10:10

Это было бы здорово и полезно ))

БГ
  • 8 января 2019 г. 10:27

При запуске приложения библиотека должна лежать рядом с исполняемым файлом. А как сделать так, чтобы библиотека лежала в папке на уровень ниже чем сам исполняемый файл?

nayk1982
  • 8 января 2019 г. 20:02

QApplication::addLibraryPath()

Можно в каталоге приложения создать файл qt.conf в котором прописать пути библиотек:

[Paths]
Libraries=./lib
Plugins=./lib

Edward Wayne
  • 4 марта 2019 г. 1:17

А можно динамическую библиотеку, скомпелированную в Visual Studio и никак не связанную с Qt, подключить в проект который разрабатывается в Qt?

Какие действия для этого нужно сделать?

Достаточно ли будет просто заменить эти строки:

#if defined(QUILIB_LIBRARY)
#  define QUILIBSHARED_EXPORT Q_DECL_EXPORT
#else
#  define QUILIBSHARED_EXPORT Q_DECL_IMPORT
#endif

на эти:

#define QUILIB_EXPORT __declspec(dllexport)

?

Evgenii Legotckoi
  • 4 марта 2019 г. 3:22

Полагаю, что да, нужно переписать экспорт, как вы написали. А подключение в Qt проекте будет аналогичным, такженаличие пути к библиотеке и заголовочные файлы. Главное, чтобы компиляторы были одной версии.

e
  • 16 ноября 2020 г. 3:32

здравствуйте! при компиляции библиотеки выскакивает окно особая программа( не удалось найти программу, укажите путь к ней), и в папке debug создается файл .dll, а .lib нет. подскажите, пожалуйста, в чем проблема.

DS
  • 16 февраля 2022 г. 5:54

Добрый день!
Очень мало информации, как писать классы с методами для компиляции в динамическую библиотеку.
Пример: в классе QuiLib дополнительно есть методы, которые могут вызываться, например

void setText(QString t) {
qlabel->setText(t);
}

то этот метод следует обьявлять как virtual, чтобы потом вызвать его где надо, верно?

Evgenii Legotckoi
  • 17 февраля 2022 г. 2:51

Нет, не верно. Модификатор virtual помечает метод класса как виртуальный, что позволяет переопределять методы при наследовании классов. К библиотекам вообще никакого отношения не имеет.

Обычно функции отдельно помечаются макросом типо QUILIBSHARED_EXPORT, но проще написать класс helper со статическими методами, ибо потом меньше проблем с линковкой и компиляцией.

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
ДА

C++ - Тест 003. Условия и циклы

  • Результат:21баллов,
  • Очки рейтинга-10
АФ

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:30баллов,
  • Очки рейтинга-10
ПЩ

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80баллов,
  • Очки рейтинга4
Последние комментарии
k
kmssr9 февраля 2024 г. 5:43
Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко5 февраля 2024 г. 12:50
Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 декабря 2023 г. 21:30
Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 декабря 2023 г. 19:38
Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 декабря 2023 г. 8:01
Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Сейчас обсуждают на форуме
P
Pisych27 февраля 2023 г. 15:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 января 2024 г. 22:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…
BlinCT
BlinCT27 декабря 2023 г. 19:57
Растягивать Image на парент по высоте Ну и само собою дял включения scrollbar надо чтобы был Flickable. Так что выходит как то так Flickable{ id: root anchors.fill: parent clip: true property url linkFile p…
Дмитрий
Дмитрий10 января 2024 г. 15:18
Qt Creator загружает всю оперативную память Проблема решена. Удалось разобраться с помощью утилиты strace. Запустил ее: strace ./qtcreator Начал выводиться весь лог работы креатора. В один момент он начал считывать фай…
Evgenii Legotckoi
Evgenii Legotckoi12 декабря 2023 г. 17:48
Побуквенное сравнение двух строк Добрый день. Там случайно не высылается этот сигнал textChanged ещё и при форматировани текста? Если решиать в лоб, то можно просто отключать сигнал/слотовое соединение внутри слота и …

Следите за нами в социальных сетях