Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Dialog UI doesn't show up if it's pointer is allocated inside a function.



  • I don't know exactly how to search this problem so I will try to describe it here. I would appreciate any pointers (ironic) on what I am missing.

    So I have a class named Calculator_Win which inherits from QMainWindow. It contains as class members various buttons etc. Some of them are connected to show a custom (Q)Dialog which has some functionality. And here lays my problem:

    class Calculator_Win : public QMainWindow
    {
    	Q_OBJECT
    public:
    	explicit Calculator_Win( QWidget* parent = nullptr );
    	virtual ~Calculator_Win() override;
    ...
    private slots:
    	void fireup_dialog( const PushButton* pbtn );
    ...
    private:
    	std::unique_ptr<Ui::Calculator_Win> m_ui;
    	std::unique_ptr<ConvertDialog>      converterDlg;
    //If the above pointer isn't allocated in Calculator_Win's Ctor, 
    //the ui doesn't show up
    
    

    Now, if I allocate converterDlg in the ctor like:

    Calculator_Win::Calculator_Win( QWidget* parent )
    	: QMainWindow { parent }
    	, m_ui {std::make_unique<Ui::Calculator_Win>()}
          	, converterDlg {std::make_unique<ConvertDialog>()}
    ...
    {
    // Body of Ctor
          	m_ui->setupUi( this );
    ....
    

    the dialog shows up later when I click my buttons. If however I don't make the converterDlg pointer to ConvertDialog a class member, but rather I allocate it like this:

    //Right now converterDlg is commented out from the class's variables
    void Calculator_Win::fireup_dialog( const PushButton* pbtn )
    {
    	auto converterDlg { std::make_unique<ConvertDialog>( pbtn, this ) };
    	converterDlg->setModal( true );
    	converterDlg->setWindowFlags( Qt::CustomizeWindowHint
    				      | Qt::WindowTitleHint | Qt::Dialog );
    
    	converterDlg->show();
    }
    

    nothing happens. The main window (Calculator_Win) is responsive. In fact, I can click any button I want or move around the window and nothing is lagging. The slot fireup_dialog is called, but the dialog is missing.

    I am developing in KDevelop and I ran this with Helgrind among other checks. I got multiple errors about

    .../src/main.cpp:17:2: Possible data race during write of size 8 at 0x8945198 by thread #1
    

    The problematic line is this:

    #include "calculator_win.h"
    
    int main( int argc, char* argv[] )
    {
    	QApplication app( argc, argv ); <----
    	qRegisterMetaType<PushButton>();
    	qRegisterMetaType<ButtonGroup>();
    
    	Calculator_Win w;
    

    Could this be irrelevant? I am thinking that maybe the pointer gets out of scope when fireup_dialog exits so nothing is show, but how the function exits if the ConvertDialog is (should be) allocated and "active"?

    Any ideas on how to debug this would be much appreciated. Thank you.


  • Moderators

    @Petross404_Petros-S said:

    I am thinking that maybe the pointer gets out of scope when fireup_dialog exits so nothing is show

    Yes, this is the cause. You're making a local unique pointer, so it deletes your dialog as soon as you get to the }.

    but how the function exits if the ConvertDialog is (should be) allocated and "active"?

    There's nothing blocking in that function. The dialog is created, shown and the function exits. If you want to block there until the dialog is closed change show() to exec(). This creates a local event loop and blocks until the dialog is closed.
    Otherwise don't put the pointer in a smart pointer and delete it manually when the dialog is closed (see e.g. the Qt::WA_DeleteOnClose attribute).



  • @Chris-Kawa said in Dialog UI doesn't show up if it's pointer is allocated inside a function.:

    There's nothing blocking in that function. The dialog is created, shown and the function exits. If you want to block there until the dialog is closed change show() to exec(). This creates a local event loop and blocks until the dialog is closed.

    Changing to exec() method solved it.

    One last question, are there are any downsides or other things I should have in mind when having a (local) second loop in my Qt app?


  • Moderators

    @Petross404_Petros-S said:

    are there are any downsides or other things I should have in mind when having a (local) second loop in my Qt app?

    Not that I can think of. Modal dialogs are designed to work that way. It just changes the flow of your application, so you need to keep that in mind, but otherwise they're pretty simple. For non-modal dialogs it's more common to use show() and let the app get back to the main event loop., but that's just because it's easier to reason about the flow of your app.
    While you could come up with more convoluted designs usually you have only your main loop most of the time, a sporadic modal dialog run with exec() and occasional brief window like a message box (yes, they also work this way). More than that would simply become hard to keep track of.



  • So the main window is blocked. The way I see this is that if for example I would like to open 2 or more dialogs at the same time, I should find another way to show them by not blocking the main loop.

    I guess if I want to use the app in this way, I should just declare the pointer to ConvertDialog as member variable and get done with it. It's a misconception I have that having many member variables is kind of a lazy solution. I mean how bloated can we allow a class to become?

    Thank you once again for your explanation. Have a nice day.


  • Moderators

    So the main window is blocked.

    Hold on a sec. Lets be clear here. Local event loop started by exec() does not block event processing in main window or any other parts of your app. Main window can't get focus, but that's because you set your dialog to be modal, not because of a local event loop. All events are processed as usual, no matter how deep on an event loop stack you are. It's just that you don't leave the scope the loop is running in.

    The way I see this is that if for example I would like to open 2 or more dialogs at the same time, I should find another way to show them by not blocking the main loop.

    Just use show() in that case, like you did. Just don't use a smart pointer on it so it doesn't delete your instances when you leave the scope.

    I guess if I want to use the app in this way, I should just declare the pointer to ConvertDialog as member variable and get done with it.

    If you need to have some sort of access to your dialog when it's shown then that's one way to do it, but if it's a standalone thing you don't need to keep any pointer to it. You can use the Qt::WA_DeleteOnClose attribute I mentioned so it deletes itself when it's closed or you can manually call deleteLater() in its close event handler.

    I mean how bloated can we allow a class to become?

    There's simply no one answer to that. From technical standpoint you can stuff your class as much as fits in the memory. A pointer or two is not a lot when you think how much RAM a random computer has. You could have no classes at all for all CPU cares. It's all just bytes and addresses to it anyway.
    The other consideration though is readability and maintainability of your code. Smaller classes compose better and are easier to understand, refactor and maintain. Things like dialogs or message boxes are temporary in nature so it's a bit silly to keep a pointer to one around when most of the time it's null, but sometimes it's just the most convenient thing to do.

    All in all it's the beautiful dance of balancing those different concerns we programmers deal with every day :)


Log in to reply