Opening a Qt dialog from within a plugin (a general-type shared library)



  • Hello,

    I am having difficulties displaying a Qt dialog from within a plugin (a dll dynamically loaded by another application). The other application is also based on Qt and constructs a QApplication object. In the main application, I open a main window (QMainWindow) and several dialogs. Then I send the handle of the main window ( void* mainWindowHandle=myMainWindow->winId() ) to the plugin (during plugin initialization).

    The plugin then tries to open (non-modally) one of its own dialog with following instructions:

    @
    pluginDialog=new CPluginDialog((QWidget*)mainWindowHandle);
    pluginDialog->setVisible(true);
    @

    Trying to make the plugin dialog visible crashes the plugin and application. This also happens when I create the plugin dialog with a NULL argument like this:

    @
    pluginDialog=new CPluginDialog(NULL);
    pluginDialog->setVisible(true);
    @

    What am I doing wrong? How can I tell the plugin that it should use the same messaging loop for its dialogs as the main application? (everything is using the same thread)

    [EDIT: code formatting, please use @-tags, Volker]



  • Might it be a problem with the project file?

    Following is what I have in the project file:

    TEMPLATE = lib
    CONFIG += shared
    QT += gui

    I also tried to create a second QApplication or QCoreApplication object, but that didn't help..



  • You cannot pass a value returned by <code>QWidget::winId()</code> as a parent. The one is a native window handle of type <code>WId</code> (not <code>void*</code>), the other is a <code>QWidget*</code>. If you want your main window to be the parent of your widget, you will have to pass a pointer to the main window, not its window id.
    @void MainWindow::initializePlugin()
    {
    plugin->initialize(this);
    }
    ...
    void Plugin::initialize(QWidget* mainWindow)
    {
    Dialog* dialog = new Dialog(mainWindow);
    dialog->show();
    }@

    You should always prefer C++ style casts (<code>static_cast</code> and <code>dynamic_cast</code>) over C style casts and <code>reinterpret_cast</code>. They won't allow you to do such horrible things as:
    [quote author="floatingWoods" date="1325597152"]
    @
    void* mainWindowHandle = myMainWindow->winId();
    pluginDialog = new CPluginDialog((QWidget*) mainWindowHandle); // WRONGEST
    @
    [/quote]



  • Thanks Lukas,

    You are right, and I corrected for that. However, I still have the same problem. I also have that problem when I create the plugin dialog with a NULL argument. The plugin initializes fine but then crashes when I call "setVisible(true)" on the created dialog.



  • Actually I noticed that the main application sometimes has the same problem (at least it appears same). Still, always using the same thread (the main thread), sometimes following works, sometimes it crashes in msg.exec():

    @
    QMessageBox msg(NULL);
    msg.setWindowTitle(title.c_str());
    msg.setText(message.c_str());
    msg.addButton(QMessageBox::Ok);
    int result=msg.exec();
    @

    is there some restriction when exec() can be called?

    [EDIT: code formatting, Volker]



  • Can you provide a small compileable example that reproduces your problem?



  • The application is very big and it would take days to simplify it to provide a small compilable example...
    But I think I found the source of the problem:

    I manage the "main application loop" myself, i.e. I call on a regular basis "myQApplication->processEvents". This is important for that application that is not purely event based. The main thread should never be blocked or trapped in "myQApplication->exec()" or similar.

    When a dialog is opened or shown indirectly by a call to "myQApplication->processEvents", which is 99% of the cases, there is no problem at all. In that case a user action triggers an event, that is processed by that call, and that opens a dialog.

    However, when I open or show a dialog somewhere else (e.g. just before or after calling "myQApplication->processEvents"), then the application crashes.

    What am I doing wrong here?

    EDIT: actually, the whole application was ported to Qt from a MSVC application. Under MSVC, above method (i.e. "a non-blocking main application loop") doesn't cause any crash or strange behaviour



  • The firsthand solution would be to use Qt programming patterns for Qt programs, and MSVC programming patterns for MSVC native programs. In other words: call application.exec(). The usual flow of control in a Qt program is that after application.exec() returns, the program shuts down. There's at maximum a few lines of clean up code after that and before returning from main().

    This way you save yourself from calling processEvents, etc.

    The Qt event loop is non blocking in the sense that the rest of your program logic is pretty much unblocked. And something in main() must block until the user quits the program, otherwise the program would quit immediately. So you will have something similar and blocking in your main method (probably a loop watching over a flag or the like).

    Additionally, make sure you always link against the same library versions and build flavors, especially make sure to not mix debug and release versions!



  • Thanks Volker!

    I will gradually shift to a "more conventional" way of handling the application, but the first priority is to keep "relative" similarity/compatibility with the old framework so that old users don't get upset.

    I noticed something strange right now:

    When I prepare my Qt application at start-up (allocating resources, creating a QMainWindow with toolbars+menu bar), I can display message boxes without any problem any time... until I call myQMainWindow->showMaximized(). After that, message boxes work fine only when called from within processEvents(). Is that a normal behaviour?



  • Are you talking about users of your application? Those do not care about internal implementations. If you're talking about users in the sense of co-developers, switching frameworks causes the need to switch habits too. There's no gain in conserving old habits in a new framework if it is "the wrong way" to do it.

    Message boxes carry their own event loop. That's why they work even if the main event loop hasn't started yet. For your main window to work you need the main event loop, it doesn't provide its own incarnation. If you do not start application.exec(), you're pretty much on your own. That's the way a Qt application is supposed to be run and you will have a hard time finding someone giving you advice on doing otherwise. It's just too much that depends on the main event loop running.

    You will run into further serious issues. So do yourself (and your users) a favor and to it "the right way" from the beginning! Learning Qt by ignoring it's rules is much like learning English from dadaistic poems - you will gain almost nothing.



  • That makes sense.

    When I am talking about users, those are also writing plugins for my application. Until now there was only one single thread that would talk to those plugins. If I switch to the "correct Qt" way, I will have to split the application into:

    • Qt thread and UI handling (rendering an openGl scene, handling the dialogs, etc.). This may block if there is another thread (see next item):
    • Working thread (doing simulation calculations). I guess message boxes can be started from there without problems?


  • No, you must not start any GUI related code from a worker thread. This must be done in the GUI thread. The good side is: you can connect a signal of the worker thread to your GUI classes and request a message box this way.

    As threaded programming is quite tricky, don't forget to read further on this topic in Qt context. At least, read an understand pepes wonderful wiki article about "Threads, Events and QObjects":/wiki/Threads_Events_QObjects.



  • Thanks a lot Volker. I will look into that article.

    Best regards and thanks again!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.