Политика конфиденциальностиКонтактыО сайтеОтзывыGitHubDonate
© EVILEG 2015-2018
Рекомендует хостинг
TIMEWEB

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

Qt, DLL, dll, Windows, dynamic

На форуме возник вопрос, как создать динамическую библиотеку и правильно её подключить в сторонний проект. Периодически такие вопросы возникают, поэтому рассмотрим один вариант создание динамической библиотеки 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() завершится только тогда, когда произойдёт соответствующее событие, которое закроет диалог.

Результат


10 октября 2018 г. 1:49

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

10 октября 2018 г. 6:59

Спасибо ))

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

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



10 октября 2018 г. 8:21

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

10 октября 2018 г. 8:25

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

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

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

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

10 октября 2018 г. 8:31

Типо как в Qt Creator?

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

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

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

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

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

10 октября 2018 г. 9:50

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

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

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

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

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

21 октября 2018 г. 15:10

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

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
ДД
13 декабря 2018 г. 16:24
Дмитрий Дубовик

C++ - Тест 005. Структуры и Классы

  • Результат:66баллов,
  • Очки рейтинга-1
13 декабря 2018 г. 16:04
Metelev

Qt - Тест 001. Сигналы и слоты

  • Результат:47баллов,
  • Очки рейтинга-6
YC
12 декабря 2018 г. 18:49
Yaroslav Chernetskyi

Qt - Тест 001. Сигналы и слоты

  • Результат:31баллов,
  • Очки рейтинга-10
Последние комментарии
V
15 декабря 2018 г. 2:06
Vlad15007

Спасибо большое!Очень помогли!
11 декабря 2018 г. 21:01
Евгений Легоцкой

Не знаю, какой-там конкретно эффект и если честно не хочется fl studio ради того, чтобы посмотреть устанавливать, но из того, что увидел в интернете. Предполагаю, что то, что вы хотите с...
V
11 декабря 2018 г. 19:25
Vlad15007

Подскажите пожалуйста ( я новичок совсем)Можно ли организовать спрайт без этого окошка (как в fl studio fruity dance)?
11 декабря 2018 г. 15:06
Евгений Легоцкой

Что интересно, если написать так from <application_name>.<module_name> import <filename> ,то PyCharm сносит крышу, если разрабатываешь в рамках проекта приложение, ко...
11 декабря 2018 г. 14:52
Илья Чичак

Тут мне тоже есть что сказать=) Сами разрабы советуют импортировать следующим образом: from <application_name> import <module_name> Стоит избегать from . import &l...;
Сейчас обсуждают на форуме
ИМ
18 декабря 2018 г. 15:29
Игорь Максимов

Доброго времени суток. Имеется модель для видео-контента (Movie), а конкретно привязана сейчас к одной модели(Compilation). Появилась необходимость добавить еще одну модель (Category) и связа...
R
18 декабря 2018 г. 12:25
RED_Spider

именно так, проблема в кодировке, а именно в отсутствии шрифтов на сервере, для меня вопрос решился в CentOS 7yum install curl cabextract xorg-x11-font-utils fontconfig всем спасибо за ...
U
18 декабря 2018 г. 10:39
Unreal_man

А вот этот коннект здесь и вовсе не нужен connect(ui->ok3, &QPushButton::clicked, this, &Widget::addToText); А как же без него? ============================== ...
m
17 декабря 2018 г. 19:03
melnik10

Спасибо, попробую!
R
16 декабря 2018 г. 14:41
RED_Spider

перевірено все працює http://doc.qt.io/qt-5/appicon.html Setting the Application Icon on Windows First, create an ICO format bitmap file that contains the icon image. This ca...
Присоединяйтесь к нам в социальных сетях

Для зарегистрированных пользователей на сайте присутствует минимальное количество рекламы