3

I'm using Qt 5 and C++ and I want to force some of my child windows to have their own task bar entries. Right now, I am able to create parentless QWidgets and use the signal-slot mechanism to close those windows when a main window (QMainWindow) is closed.

However as I add more and more parents and childs this whole signal-slot technique will get tedious (won't it?) and I am sure that Qt already has a feature I can use for this. I've seen this; Primary and Secondary Windows section talks about what I'm trying to do. It says:

In addition, a QWidget that has a parent can become a window by setting the Qt::Window flag. Depending on the window management system such secondary windows are usually stacked on top of their respective parent window, and not have a task bar entry of their own.

I can see that I need to set my QWidgets as Primary Windows but I don't exactly know how.

So I tried:

// when a QMainWindow's (this) push button is clicked:
QWidget* newWindow = new QWidget(this, Qt::Window);
newWindow->show();

It doesn't give me the behavior I want. How can I set newWindow as a Primary Window while still keeping this as its parent?

Deniz
  • 509
  • 7
  • 26

2 Answers2

1

I don't now native method to do it in Qt. But you can use for it signal/slot it's definitely not create a serious burden on the processor until you have at least a thousand windows. For it you can implement your own child parent mechanizme.

For example:

class myOwnWidget: public QWidget{
    Q_OBJECT
public:
    myOwnWidget(myOwnWidget* parent = 0):
        QWidget(){
        if(parent){
            connect(parent,SIGNAL(close()), this,SLOT(deleteLater()));
        }
    }

    void closeEvent(QCloseEvent* e){
        emit close();
        QWidget::closeEvent(e);
    }

signals:
    void close();
};

class PrimaryWindow : public myOwnWidget{
    PrimaryWindow(myOwnWidget *parent):
        myOwnWidget(parent)
    {
        //your constructor here
    }

}

using:

PrimaryWindow * rootWindows = new PrimaryWindow();
PrimaryWindow * childWin = new PrimaryWindow(rootWindows);

In code PrimaryWindow create without Qt-parent, but for closing child windows you should inherit all your windows from myOwnWidget.

Hope it help.

Konstantin T.
  • 994
  • 8
  • 22
  • 1
    why do you connect to `deleteLater()` slot and not `close()`? maybe you should have your signal's name as `closed()` instead? – Mike Sep 30 '16 at 16:12
  • I got it running but to get the exact behavior I wanted, I had to take @Mike's advice and first rename the signal to something different than `close()` and connect it to the slot `close()`. When I arrange like this I am able to inherit new windows from `PrimaryWindow` while keeping the behavior I want. The signal-slot mechanism described here is a lot more clever than what I had in my classes and it does what I want; however I'll wait for a while before I accept this answer because I would really like to know if it could be done using window flags. Thank you both – Deniz Sep 30 '16 at 17:33
  • 1
    @Deniz, In order to emulate the `QObject` ownership, you might want to connect `destroyed` from the parent to `deleteLater` slot. (something like `connect(parent,SIGNAL(destroyed()), this,SLOT(deleteLater()));`) in addition to connecting our `closed()` signal to `close()` slot. – Mike Sep 30 '16 at 17:56
  • I've been playing around with this and found out that this method only allows windows to be `QWidget`s. For example, when you need a `QMainWindow` then this method starts to cause trouble. I tried having a type `MyWidget` which would inherit `QWidget` and a `MyMainWindow` which would inherit `QMainWindow`. However it does not work good because in both `My` types, constructors need to be able to accept the parent types `MyWidget` **and** `MyMainWindow`, which I couldn't achieve. Is it just me (as a newbie) or are we in a circular dependency kind of situation here? – Deniz Oct 01 '16 at 15:05
  • @Deniz , If your problem is about circular dependency, you just need to use [forward declaration](https://stackoverflow.com/questions/553682/when-can-i-use-a-forward-declaration). – Mike Oct 01 '16 at 20:50
1

Heavily based on posters Konstantin T.'s and Mike's ideas, I have developed two classes, MyWidget and MyMainWindow, which can use themselves and each other in their constructors to create child windows which will have their own task bar entries while still acting as children to their parent windows (i.e. getting automatically closed and destroyed upon termination of the parent window). MyWidget replaces QWidget and MyMainWindow replaces QMainWindow if such a behavior is desired.

my_widget.h:

#ifndef MY_WIDGET_H
#define MY_WIDGET_H

#include <QWidget>
#include <QMainWindow>

class MyMainWindow;

class MyWidget : public QWidget{

    Q_OBJECT

public:
    MyWidget(MyWidget* parent = 0){
        if(parent){
            connect(parent, SIGNAL(window_closed()),
                    this, SLOT(close()));
            connect(parent, SIGNAL(destroyed(QObject*)),
                    this, SLOT(deleteLater()));
        }
    }

    MyWidget(MyMainWindow* parent);

    void closeEvent(QCloseEvent* event){
        emit window_closed();
        QWidget::closeEvent(event);
    }

signals:
    void window_closed();
};

class MyMainWindow : public QMainWindow{

    Q_OBJECT

public:
    MyMainWindow(MyMainWindow* parent = 0){
        if(parent){
            connect(parent, SIGNAL(window_closed()),
                    this, SLOT(close()));
            connect(parent, SIGNAL(destroyed(QObject*)),
                    this, SLOT(deleteLater()));
        }
    }

    MyMainWindow(MyWidget* parent){
        connect(parent, SIGNAL(window_closed()),
                this, SLOT(close()));
        connect(parent, SIGNAL(destroyed(QObject*)),
                this, SLOT(deleteLater()));
    }

    void closeEvent(QCloseEvent* event){
        emit window_closed();
        QMainWindow::closeEvent(event);
    }

signals:
    void window_closed();
};

#endif // MY_WIDGET_H

my_widget.cpp:

#include "my_widget.h"

MyWidget::MyWidget(MyMainWindow* parent){
    connect(parent, SIGNAL(window_closed()),
            this, SLOT(close()));
    connect(parent, SIGNAL(destroyed(QObject*)),
            this, SLOT(deleteLater()));
}

Example main.cpp:

#include <QApplication>
#include "my_widget.h"

int main(int argc, char* argv){
    QApplication a(argc, argv);

    MyWidget mw1{new MyWidget};
    mw1.setWindowTitle("ctor: MyWidget(MyWidget*)");
    mw1.show();

    MyMainWindow mmw1{&mw1};
    mmw1.setWindowTitle("ctor: MyMainWindow(MyWidget*)");
    mmw1.show();

    MyMainWindow mmw2{&mmw1};
    mmw2.setWindowTitle("ctor: MyMainWindow(MyMainWindow*)");
    mmw2.show();

    MyWidget mw2{&mmw2};
    mw2.setWindowTitle("ctor: MyWidget(MyMainWindow*)");
    mw2.show();
    return a.exec();
}

So the window chain is: mw1 -> mmw1 -> mmw2 -> mw2 where any window that is closed will also destroy all windows to its right and all windows in the chain will have their own task bar entries.

I'm sure there are more elegant ways of defining the constructors in my_widget.h, but as a newbie this worked for me to obtain the exact behavior I needed. I'd appreciate to see better practices on my_widget.h.

Deniz
  • 509
  • 7
  • 26