Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Update: Forum Guidelines & Code of Conduct


    Qt World Summit: Early-Bird Tickets

    Ui in external library

    General and Desktop
    library gui application
    2
    8
    3623
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • J
      Joachim Holst last edited by

      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
      
      1 Reply Last reply Reply Quote 0
      • JKSH
        JKSH Moderators last edited by

        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 that QApplication::exec() is a blocking function; it will not return until you call QApplication::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.

        Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

        1 Reply Last reply Reply Quote 0
        • J
          Joachim Holst last edited by

          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!

          JKSH 1 Reply Last reply Reply Quote 0
          • JKSH
            JKSH Moderators @Joachim Holst last edited by

            @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?

            Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

            J 1 Reply Last reply Reply Quote 0
            • J
              Joachim Holst @JKSH last edited by

              @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()
                      }
                   }
                 }
              }
              1 Reply Last reply Reply Quote 0
              • JKSH
                JKSH Moderators last edited by

                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.

                Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                J 1 Reply Last reply Reply Quote 0
                • J
                  Joachim Holst @JKSH last edited by

                  @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.

                  1 Reply Last reply Reply Quote 0
                  • J
                    Joachim Holst last edited by

                    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.

                    1 Reply Last reply Reply Quote 1
                    • First post
                      Last post