- 1. Schritt 1
- 2. Schritt 2
- 3. Schritt 3
- 4. Schritt 4
- 5. Schritt 5
- 6. Schritt 6
- 7. Schritt 7
- 1. Projektstruktur
- 2. QuiLib.pro
- 3. quilib_global.h
- 4. QuiLib.h
- 5. QuiLib.cpp
- 8. Schritt 8
- 9. Schritt 9
- 10. Schritt 10
- 11. Schritt 11
- 12. Schritt 12
- 13. Schritt 13
- 14. Schritt 14
- 15. Schritt 15
- 16. Schritt 16
- 17. Schritt 17
- 18. Schritt 18
- 19. Ergebnis
Im Forum tauchte die Frage auf, wie man eine dynamische Bibliothek erstellt und korrekt mit einem Drittprojekt verbindet. Solche Fragen tauchen regelmäßig auf, also betrachten wir eine Option, nämlich das Erstellen einer dynamischen DLL-Bibliothek für Windows mit den Standardassistenten in Qt Creator.
In diesem Fall wird die Option nicht berücksichtigt, wenn das Projekt in Teilprojekte aufgeteilt wird, die als Bibliotheken zusammengestellt und dann mit dem Hauptprojekt verbunden werden. Da dies dynamische interne Bibliotheken des Projekts sein werden. Lassen Sie uns eine externe Bibliothek erstellen, die theoretisch in Form von Binärdateien verteilt werden kann.
Lassen Sie uns zwei Projekte erstellen:
- QuiLib – Dies ist eine externe dynamische Bibliothek, die ein Dialogfeld enthält. Dieses Dialogfeld wird im Hauptprojekt geöffnet.
- WithDynamicLibrary – das Projekt, das nur zum Verbinden dieser dynamischen Bibliothek verwendet wird.
Schritt 1
Lassen Sie uns die Projekterstellung im Qt Creator-Menü auswählen und den Typ unseres Projekts auswählen. Es wird eine C++ Bibliothek sein.
Schritt 2
Schreiben wir den Namen des Projekts und seinen Standort auf
Schritt 3
Lassen Sie uns Kits für den Aufbau des Projekts auswählen.
Hier gibt es einen sehr wichtigen Punkt, den Anfänger vergessen können. Wenn Sie ein Projekt mit einem Compiler einer bestimmten Version erstellen, können Sie diese Bibliotheken nur in einem Projekt verwenden, das von einem Compiler derselben Version erstellt wird.
Ich erstelle diese Bibliothek mit dem Compiler MSVC2017.
Schritt 4
Wählen Sie die gewünschten Module aus. Für unsere Bibliothek reicht die Grundfunktionalität aus.
Schritt 5
Geben wir der Bibliotheksklasse, die darin verwendet wird, einen Namen. Der Name ist in diesem Fall derselbe wie der Name der Bibliothek selbst. Aber du kannst dich ändern. Es ist nicht wesentlich.
Schritt 6
Verwenden Sie ein Versionskontrollsystem? Fügen Sie also das Projekt unter Versionskontrolle hinzu. Wenn nicht, dann nichts tun. Und baue einfach die Bibliothek fertig.
Schritt 7
Schauen wir uns die Projektdateien an und ändern sie ein wenig.
Projektstruktur
QuiLib.pro
Diese Datei enthält die Information, dass es sich um eine Bibliothek handelt. Hier in dieser Zeile.
TEMPLATE = lib
Hier ist der vollständige Profilcode
#------------------------------------------------- # # 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
Header zum Definieren von Exportdefinitionen in der Bibliothek. Klassen, die mit einer Exportdefinition gekennzeichnet werden, stehen zur Verwendung außerhalb der Bibliothek zur Verfügung.
#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
Lassen Sie uns die Header-Datei des Dialogfelds leicht korrigieren, da wir einen Dialog benötigen, was bedeutet, dass die Klasse in dieser Header-Datei von QDialog geerbt werden muss.
#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
Wir werden auch eine Implementierung des Dialogfeldkonstruktors schreiben, die uns mit QLabel mitteilt, dass dies ein Dialogfeld aus einer externen Bibliothek ist.
#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)); }
Schritt 8
Lassen Sie uns das Projekt in den Versionen Debug und Release kompilieren.
Hier ist beispielsweise, was sich im Release-Verzeichnis der Bibliotheksassembly befinden wird. Von diesen Dateien benötigen wir nur die Dateien QuiLib.dll und QuiLib.lib. Zusätzlich zu diesen Dateien benötigen wir auch Header-Dateien aus dem Projekt selbst, aber dazu später mehr.
Schritt 9
Erstellen eines Projekts, das diese dynamische Bibliothek verwendet. Der Erstellungsprozess erfolgt standardmäßig über einen Assistenten in Qt Creator. Sie müssen Application on Qt auswählen.
Fügen Sie den Projektnamen und den Standort hinzu
Schritt 10
Geben Sie einen Bausatz an.
Schritt 11
Lassen Sie uns den Namen der Klasse des Hauptfensters der Anwendung eingeben und auch angeben, von welcher Klasse geerbt werden soll. Ich habe mich für QWidget entschieden.
Schritt 12
Erneutes Auswählen eines Versionskontrollsystems und Abschluss des Projekterstellungsprozesses.
Schritt 13
Projektstruktur
Im Verzeichnis dieses Projekts erstellen wir ein Verzeichnis QuiLib , in dem wir die Verzeichnisse debug, release, include. ablegen.
Diese Verzeichnisse enthalten die kompilierten Bibliotheken QuiLib.dll und QuiLib.lib der Debug-Version bzw. der Release-Version. Das Include-Verzeichnis enthält die Header-Dateien QuiLib.h und quilib_global.h.
Das heißt, wir haben die Situation dargestellt, in der wir die kompilierte Bibliothek jemandem übergeben haben, damit er sie verbinden und verwenden kann.
Schritt 14
Hinzufügen der Bibliothek zum Projekt mithilfe des Assistenten. Natürlich können Sie alles manuell vorschreiben, aber wenn Sie an Ihren Fähigkeiten zweifeln, und das stimmt, sonst hätten Sie diesen Artikel nicht gelesen, dann verwenden wir einen Assistenten.
Schritt 15
Wir wissen, dass die Bibliothek extern ist
Schritt 16
Und auch, dass wir es nur für Windows verwenden werden. Es ist so konfiguriert, dass sich die Versionsdateien Debug und Release in unterschiedlichen Verzeichnissen ohne Debug-Bibliothekspräfixe befinden. Ich habe sie nur nicht eingerichtet. Es reicht aus, eine der .lib *-Bibliotheken entweder im Verzeichnis debug oder release anzugeben. Der Pfad zu einer anderen Version wird automatisch hinzugefügt. Sie müssen auch das Verzeichnis angeben, in dem sich die Header-Dateien befinden. Dies ist traditionell ein include**-Verzeichnis.
Schritt 17
Wir vervollständigen die Addition
Schritt 18
Sie müssen eine Methode schreiben, die ein Dialogfeld aus einer externen Bibliothek aufruft. Sehen Sie sich jedoch zuerst an, wo die Verbindungszeichenfolgen für Drittanbieterbibliotheken, die wir auf der Assistentenseite in Schritt 17. gesehen haben, hinzugefügt wurden.
Sehen wir uns nun das Profil unseres Projekts an, das die dynamische Bibliothek verwenden wird.
Mit 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
Dies sind die allerletzten Zeilen in dieser Datei.
Widget.ui
Über den Grafikdesigner fügen wir dem Hauptfenster eine Schaltfläche hinzu, durch deren Drücken ein Dialog aus der externen Bibliothek aufgerufen wird.
Widget.h
Lassen Sie uns einen Slot schreiben, um den Klick auf die Schaltfläche zu verarbeiten.
#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
Und jetzt verarbeiten wir den Schaltflächenklick und rufen das Dialogfeld aus der externen Bibliothek auf.
#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(); }
Beachten Sie, dass Sie das Dialogfeld mit der Methode exec() aufrufen müssen, um die interne Schleife des Dialogfelds zu starten, die auf Ereignisse wartet. Andernfalls wird der Dialog sofort geschlossen, da der Slot funktioniert und der Dialog in diesem Fall auf dem Methodenstapel erstellt wurde, und nach Abschluss der Methode wird der Dialog zerstört. Und die Methode exec() wird nur abgeschlossen, wenn das entsprechende Ereignis eintritt, wodurch der Dialog geschlossen wird.
Хороший урок, все подробно расписано. Такой вопрос: версия Qt для дин.библиотеки не обязательно должна совпадать с версией Qt проекта, который эту библиотеку использует?
Спасибо ))
Из того, что я читал в документации, следует, что библиотеки Qt бинарно совместимы по минорным версиям. То есть, если проект работал с Qt 5.6, то можно поменять библиотеки на Qt 5.7 и по прежнему всё будет работать. На практике, конечно, не всегда всё гладко проходит. То есть по идее, если динамическая библиотека использует Qt 5.6, а подключили её в проект с Qt 5.7, то должно работать. Но опять же оговорюсь, на практике может выйти иначе, особенно, если динамическая библиотека использовала Qt 5.7, а подключили проект на Qt 5.6. Как минимум мождете оказаться, что в Qt 5.6 в каком-то классе отсутствуют некоторые методы.
То есть теоретически возможно, практически, как карта ляжет.
Вот какой вопрос возник: для запуска программы вне Qt приходится тащить с ехе'шником кучу dll. А для использования созданной dll не придется ли тащить с собой всё те же Qt5Core.dll, Qt5Gui.dll, Qt5Widgets.dll...? Особенно если дальнейшее использование созданной dll планируется без участия Qt
Погодите. Если речь идёт о библиотеке, которая использует Qt, от естественно, что ей понадобятся все те модули, от которых зависит бибилотека. Например в данном примере используются модули Qt5Core, Qt5Gui, Qt5Widgets, соответсвенно их тоже придётся тащить с собой. Если же вы создаёте библиотеку без участия Qt, то и модули Qt не будут нужны.
Вы не можете запланировать использование библиотеки без Qt, если она использует модули Qt, но если вы отказываетесь от использования Qt в библиотеке, то тогда получаете возможность не тащить все выше перечисленные модули, поскольку библиотека от них не зависит тогда.
Круто было бы прочитать про приложение с подключаемыми плагинами.
Типо как в Qt Creator?
Самому бы интересно было о таком почитать. В данный момент я бы мог написать только о написании плагинов для Qt Designer. С этим есть некоторый опыт.
ну типа того, создание программы, функционал которой можно расширять плагинами, и, в перспективе, создание API.
О плагинах к QtCreator в целом, тоже интересно.
Если и начинать писать о плагинах, то нужно тогда с Qt Creator начинать, там наверняка будет одинаковый принцип, но по Qt Creator хотя бы информация есть.
наверняка, так и есть)
В принципе у меня есть опыт реализации плагинов, могу что-нибудь накропать как будет время
Это было бы здорово и полезно ))
При запуске приложения библиотека должна лежать рядом с исполняемым файлом. А как сделать так, чтобы библиотека лежала в папке на уровень ниже чем сам исполняемый файл?
QApplication::addLibraryPath()
Можно в каталоге приложения создать файл qt.conf в котором прописать пути библиотек:
А можно динамическую библиотеку, скомпелированную в Visual Studio и никак не связанную с Qt, подключить в проект который разрабатывается в Qt?
Какие действия для этого нужно сделать?
Достаточно ли будет просто заменить эти строки:
на эти:
?
Полагаю, что да, нужно переписать экспорт, как вы написали. А подключение в Qt проекте будет аналогичным, такженаличие пути к библиотеке и заголовочные файлы. Главное, чтобы компиляторы были одной версии.
здравствуйте! при компиляции библиотеки выскакивает окно особая программа( не удалось найти программу, укажите путь к ней), и в папке debug создается файл .dll, а .lib нет. подскажите, пожалуйста, в чем проблема.
Добрый день!
Очень мало информации, как писать классы с методами для компиляции в динамическую библиотеку.
Пример: в классе QuiLib дополнительно есть методы, которые могут вызываться, например
то этот метод следует обьявлять как virtual, чтобы потом вызвать его где надо, верно?
Нет, не верно. Модификатор virtual помечает метод класса как виртуальный, что позволяет переопределять методы при наследовании классов. К библиотекам вообще никакого отношения не имеет.
Обычно функции отдельно помечаются макросом типо QUILIBSHARED_EXPORT, но проще написать класс helper со статическими методами, ибо потом меньше проблем с линковкой и компиляцией.