Ui in external library
-
Hi!
I'm somewhat new to QT, wrote a few applications with it some decade ago, but now I'm writing something useful ;-)I've mostly been working with C and structs and device drivers the last several years, so I'm a tad influenced by that.
However, I want to create a loadable library that will contain a UI that should be displayed in a frame.
Loading the library and creating it is no problem. That works like a charm, but actually instantiating the small ui (QWidget with a button), things go very wrong and I get:
"QWidget: Must construct a QApplication before a QWidget" when executing this.My guess is that I'm missing something simple (since i've been trying to fix it for way too long :-( ).
I really can't understand why I get the error above, since I believe that I create an instance of the library witin the current qApplication context.
Could anyone here please give me a tip and explain what my primitive brain is missing here?Usable code to demonstrate what I do is below of course ;-)
Base class for UI library
#ifndef __UIDEVICE_H__ #define __UIDEVICE_H__ #include <QtCore/qglobal.h> #include <QWidget> #if defined(UIDEVICE) # define UIDEVICESHARED_EXPORT Q_DECL_EXPORT #else # define UIDEVICESHARED_EXPORT Q_DECL_IMPORT #endif class UIDEVICESHARED_EXPORT UiDevice { public: virtual ~UiDevice() {}; virtual void display() = 0; }; #endif /* __UIDEVICE_H__ */
Derived class:
#define UITEST_H #include <QWidget> #include <uidevice.h> namespace Ui { class UiTest; } class UiTest : public QWidget, public UiDevice { Q_OBJECT public: explicit UiTest(QWidget *parent = 0); ~UiTest(); void display(); private: Ui::UiTest *ui; }; #endif // UITEST_H
C++ code:
#include "ui_uitest.h" // Extern function which is called when loading the library extern "C" { UIDEVICESHARED_EXPORT UiDevice *getUiDevice(QWidget *parent) { return new UiTest(parent); } } UiTest::UiTest(QWidget *parent) : QWidget(parent), ui(new Ui::UiTest) { ui->setupUi(this); } UiTest::~UiTest() { delete ui; } void UiTest::display() { }
QLibrary load snippet:
typedef UiDevice *(*uiDevice)(QWidget *parent); uiDevice u = (uiDevice) plugin.resolve("getUiDevice"); if (u) { qDebug() << "Found a UI Device"; // Graphwindow is an empty QDialog containing a QFrame named frame GraphWindow *g = new GraphWindow; QWidget *w = g->getHwControlWidget(); if (g) UiDevice *uid = u(g); g->show(); }
And the pro file for the library uitest.so:
QT += widgets TARGET = uitest TEMPLATE = lib CONFIG += plugin CONFIG += no_plugin_name_prefix CONFIG += shared INCLUDEPATH += ../ ../interfaces DESTDIR = ../plugins DEFINES += UIDEVICE FORMS += \ uitest.ui HEADERS += \ uitest.h SOURCES += \ uitest.cpp
-
Hi @Joachim-Holst,
The error message means that you need to create a
QApplication
object before you create any widgets.Furthermore, you need to run the QApplication's event loop (meaning, you need to call
QApplication::exec()
) in order to use widgets. The tricky part is thatQApplication::exec()
is a blocking function; it will not return until you callQApplication::quit()
. To create a GUI from a loadable library, you probably need to create your QApplication and control your widgets in a separate thread. See http://stackoverflow.com/questions/22289423/how-to-avoid-qt-app-exec-blocking-main-thread/22290909#22290909 for details. -
Hi @JKSH!
Thanks for the info, and it may solve my problem (haven't tried it yet), but it doesn't quite explain why my loadable libraries get allocated to a different thread.
What is the differente between including the .ui in the code that load the library and greating the UI there (which works flawlessly) and by letting a module that is loaded do the same thing? If I create another class that will be allocated by new in the same code that loads the library, it will work as expected.Could you or someone perhaps elaborate a bit on this?
BRs,
/Jocke! -
@Joachim-Holst said:
it doesn't quite explain why my loadable libraries get allocated to a different thread.
To be clear: Your libraries don't automatically get allocated to a different thread. You need to start a new thread, and you must ensure that your QApplication is created in that thread.
What is the differente between including the .ui in the code that load the library and greating the UI there (which works flawlessly) and by letting a module that is loaded do the same thing? If I create another class that will be allocated by new in the same code that loads the library, it will work as expected.
I'm afraid I don't understand what your description. Can you post some code to explain what you mean?
-
@JKSH said:
@Joachim-Holst said:
it doesn't quite explain why my loadable libraries get allocated to a different thread.
To be clear: Your libraries don't automatically get allocated to a different thread. You need to start a new thread, and you must ensure that your QApplication is created in that thread.
Well, I don't want my libraries to run in a different thread. I want then to run within the currently created and running QApplication context.
What is the differente between including the .ui in the code that load the library and greating the UI there (which works flawlessly) and by letting a module that is loaded do the same thing? If I create another class that will be allocated by new in the same code that loads the library, it will work as expected.
I'm afraid I don't understand what your description. Can you post some code to explain what you mean?
I have a standard QT project created with QT Creator. This has generated a main.cpp (which creates and starts a QApplication instance. It has also created a mainwindow.cpp file just like any normal QT project does. In this mainwindow.cpp (which does display a mainWindow, I load my libraries. If I take the GUI which is created for my uitest library (code included in original post), and move it to this mainwindow.cpp file, I can display the GUI. If the same GUI is left in the library which as previously mentioned is loaded in tha mainwindow.cpp file, I get this error :-(
As far as I can see and understand, the libraries are loaded within the same thread that executes the mainwin GUI.I'll add the code (placed in mainwinow.cpp) that loads the library:
void MainWindow::loadPlugins(void) { QDir pluginsDir; pluginsDir.cd("plugins"); typedef UiDevice *(*uiDevice)(QWidget *parent); foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { QLibrary plugin("plugins/" + fileName); if (plugin.load()) { qDebug() << "Loaded plugin " << fileName; uiDevice u = (uiDevice) plugin.resolve("getUiDevice"); if (u) { qDebug() << "Found a UI Device"; UiDevice *uid = u(this); uid->show() } } } }
-
Ah, I see. So your "main" application is already based on Qt, and it already created a QApplication object. In that case, ignore what I said about threads before.
I can't immediately see why you'd get the error message in your original post. What is the exact line that triggers that message?
By the way, for future reference, QPluginLoader is much more robust than QLibrary for creating and loading Qt-based plugins. It performs various error checks for you automatically.
-
@JKSH said:
Ah, I see. So your "main" application is already based on Qt, and it already created a QApplication object. In that case, ignore what I said about threads before.
Yup, that's correct ;-)
I can't immediately see why you'd get the error message in your original post. What is the exact line that triggers that message?
By the way, for future reference, QPluginLoader is much more robust than QLibrary for creating and loading Qt-based plugins. It performs various error checks for you automatically.
I was looking a QPluginLoader, but due to several specific reasons, I want to link the main application and libraries statically with QT and QPluginLoader only supports dynamically linked applications. At least according to the documentation :-(
As a side note, I tried using dlopen instead, since it seems that some magic is going on with QLibrary (at least when skimming the sorce code) like refcounting and other stuff. Initially I thought that QLibrary was basically a wrapper for the platform specific way of loading .so or .dll files.
But, changing to dlopen doesn't solve any problem either :-( Seems to be more of an intricate problem :-(
Thanks for your reply though.
-
I'm not sure anyone cares about this anymore, but I believe I know what the problem is.
When the plugin class is created, it is actually created outside the currently executing QApplication (via dlopen) and the code is running in a different thread. By reading up a bit on dlopen, it seems that the memory where the library is created comes from a different pool and that all libraries (used like this at least), should make sure to implement a function to destroy themselves.
Not sure if this makes any sense for someone, but it does for me.
Now, I just need to figure a way to get the libraries thread to join with the man QApplication that is already running.