Форумда динамикалық кітапхананы қалай құру және оны үшінші тарап жобасына дұрыс қосу туралы сұрақ туындады. Мерзімді түрде осындай сұрақтар туындайды, сондықтан бір нұсқаны қарастырайық - Qt Creator стандартты шеберлерінің көмегімен Windows үшін динамикалық кітапхана dll жасау.
Бұл жағдайда жоба кітапханалар ретінде құрастырылған, содан кейін негізгі жобаға қосылған қосалқы жобаларға бөлінген кезде опция қарастырылмайды. Өйткені бұл жобаның динамикалық ішкі кітапханалары болады. Теориялық тұрғыдан екілік файлдар түрінде таралатын сыртқы кітапхананы құрайық.
Екі жобаны жасайық:
- QuiLib - бұл бір диалогтық терезеден тұратын сыртқы динамикалық кітапхана болады. Бұл диалогтық терезе негізгі жобада ашылады.
- 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 нұсқаларында құрастырайық.
Мысалы, кітапхана жинағының шығарылым каталогында не болады. Бұл файлдардың ішінен бізге тек QuiLib.dll және QuiLib.lib. файлдары ғана қажет. Бұл файлдардан басқа, бізге жобаның өзінен алынған тақырып файлдары да қажет болады, бірақ бұл туралы кейінірек.
9-қадам
Осы динамикалық кітапхананы пайдаланатын жобаны жасау. жасау процесі Qt Creator шебері арқылы стандартты болады. Qt бойынша қолданбаны таңдау керек.
Жоба атауы мен орнын қосыңыз
10-қадам
Құрастыру жинағын көрсетіңіз.
11-қадам
Қолданбаның негізгі терезесінің класының атын енгізейік, сонымен қатар қай классты мұрагер ету керектігін көрсетейік. Мен QWidget таңдадым.
12-қадам
Нұсқаларды басқару жүйесін қайтадан таңдау және жобаны жасау процесін аяқтау.
13-қадам
Жоба құрылымы
Осы жобаның каталогында біз QuiLib каталогын жасаймыз, онда біз debug, release, include. каталогтарын орналастырамыз.
Бұл каталогтарда сәйкесінше жөндеу нұсқасының және шығарылым нұсқасының QuiLib.dll және QuiLib.lib құрастырылған кітапханалары болады. Қосу каталогында QuiLib.h және quilib_global.h. тақырып файлдары болады.
Яғни, жинақталған кітапхананы біреуге жалғап, пайдалансын деп тапсырған жағдайды таныстырдық.
14-қадам
Шебердің көмегімен жобаға кітапхананы қосу. Әрине, сіз бәрін қолмен жаза аласыз, бірақ егер сіз өзіңіздің қабілеттеріңізге күмәндансаңыз және бұл шындық болса, әйтпесе сіз бұл мақаланы оқымас едіңіз, онда біз шеберді қолданамыз.
15-қадам
Біз кітапхананың сыртқы екенін білеміз
16-қадам
Сондай-ақ біз оны тек Windows үшін қолданамыз. Ол Debug және Release нұсқа файлдары ешқандай отладка кітапханасының префикстері жоқ әртүрлі каталогтарда болатындай конфигурацияланған. Мен жай ғана оларды орнатпадым. .lib * кітапханаларының бірін debug немесе release каталогында көрсету жеткілікті. Басқа нұсқаға жол автоматты түрде қосылады. Сондай-ақ тақырып файлдары орналасқан каталогты көрсету керек. Бұл дәстүрлі түрде қосу** каталогы.
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() әдісі диалогты жабатын сәйкес оқиға орын алғанда ғана аяқталады.
Хороший урок, все подробно расписано. Такой вопрос: версия 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 со статическими методами, ибо потом меньше проблем с линковкой и компиляцией.