Evgenii Legotckoi
Evgenii Legotckoi09 жовтня 2018 р. 15: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 файл нашого проекту, який використовуватиме динамічну бібліотеку.

За допомогою DynamicLibrary.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
  • 09 жовтня 2018 р. 21:49

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

Evgenii Legotckoi
  • 10 жовтня 2018 р. 02: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 р. 04:21

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

Evgenii Legotckoi
  • 10 жовтня 2018 р. 04:25

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

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

o
  • 10 жовтня 2018 р. 04:28

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

Evgenii Legotckoi
  • 10 жовтня 2018 р. 04:31

Типо как в Qt Creator?

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

o
  • 10 жовтня 2018 р. 05:46

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

o
  • 10 жовтня 2018 р. 05:48

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

Evgenii Legotckoi
  • 10 жовтня 2018 р. 05:50

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

o
  • 10 жовтня 2018 р. 09:16

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

B
  • 20 жовтня 2018 р. 12:51

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

Evgenii Legotckoi
  • 21 жовтня 2018 р. 11:10

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

БГ
  • 08 січня 2019 р. 11:27

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

nayk1982
  • 08 січня 2019 р. 21:02

QApplication::addLibraryPath()

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

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

Edward Wayne
  • 04 березня 2019 р. 02: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
  • 04 березня 2019 р. 04:22

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

e
  • 16 листопада 2020 р. 04:32

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

DS
  • 16 лютого 2022 р. 06:54

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

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

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

Evgenii Legotckoi
  • 17 лютого 2022 р. 03:51

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

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

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
AD

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

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 01:37

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

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 01:29

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

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 11:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 14:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 06:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 03:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 09:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Слідкуйте за нами в соціальних мережах