Best way to make 2 differents subclasses communicate



  • Hello !

    I'm subclassing QMainWindow as MainWindow and QWidget as ConnectionWidget which have to be a central widget. I want my centralWidgets being able to change the QStatusBar from MainWindow easily, what is the best way to do it ?

    Here is what I thought about: I've added to MainWindow a slot (MainWindow::showMessage(...), which calls MainWindow->statusBar()->showMessage(...)) which will be called by the central widgets (here: ConnectionWidget). It seems a little ugly, because I HAVE to store the parent in ConnectionWidget which MUST be a MainWindow, and I've been told to use delegates instead.

    So here is my question: Is my approach a good way, are delegates better for this, or should I reimplement "setCentralWidget" to connect the children signals instead of doing it IN the children, or any other?

    Thank you for your help.

    -- EDIT --
    I can't connect my ConnectionWidget directly to MainWindow->statusBar() because when a message timeouts, I show the last permanent message, which is stored in MainWindow.


  • Lifetime Qt Champion

    Hi,

    Why not have your ConnectionWidget emit a signal with a QString parameter and connect it to the QMainWindow statusbar slot showMessage() ?

    MainWindow.cpp
    @
    ConnectionWidget *connectionWidget = new ConnectionWidget;
    connect(connectionWidget, SIGNAL(messageToShow(QString)), statusBar(), SLOT(showMessage(QString)));@

    Hope it helps

    EDIT:forgot showMessage QString parameter



  • [quote author="SGaist" date="1365676850"]Hi,
    Why not have your ConnectionWidget emit a signal with a QString parameter and connect it to the QMainWindow statusbar slot showMessage() ?
    [/quote]

    Actually, this is what I did first. The thing is I want to handle general status like "Ready" and temporary status like "Field can't be empty", and when the temporary is cleared, I show back the last message. I have a QString attribute to store it in the MainWindow. If I connect directly the parent's statusBar, I have to implement the "last message" system in the children which is not logical for me.

    Thank you anyway.


  • Lifetime Qt Champion

    Field empty error should be shown directly besides your mandatory input, it would be clearer for the user.

    As for managing temporary/constant message, if your temporary message are "time based" you can use showMessage(myString, myTimeoutValue). If it's a bit more complex you can either make a QStatusBar subclass and handle that there, make an object that is a "man in the middle" that handles that, or use your MainWindow for that purpose.

    Hope it helps



  • This is the point, I want to know if there is a "common" or "classic" pattern for this sort of interactions.

    Currently, here is what I have:
    @// ConnectionWidget.hpp
    signals:
    void changeStatus(QString message, int timeout);@

    @// ConnectionWidget.cpp
    connect(this, SIGNAL(changeStatus(QString, int)),
    this->m_parent->statusBar(), SLOT(showMessage(QString, int)));
    emit this->changeStatus("Ready", 0);@

    @ // MainWindow.cpp
    if (timeout)
    this->m_oldStatusMessage = this->statusBar()->currentMessage();
    else
    this->m_oldStatusMessage.clear();
    this->statusBar()->showMessage(message, timeout);@

    The thing here is that the children have to connect themselves to the parent status bar, my question is more "is it a good way to do it" instead for example, make the parent listen to the children signals ? The correct way to make this. Parent listen child(ren), Children connect to parent, delegate, ...?

    Thank you for your answers.


  • Lifetime Qt Champion

    Make these connections in MainWindow.

    ConnectionWidget should not care of where the signal is going. On the other hand MainWindow knows what to do with this signal so it's his job to do the connection.

    This keeps your classes decoupled.



  • Here we are. If the MainWindow makes the connection itself, it means it has to know the type of the child.

    As the "childs" are subclasses of QWidget, do I have to subclass QWidget to add the signals ? Then force MainWindow to only accept "MyOwnWidget *" by overloading "setCentralWidget" ?

    I ask this because if I don't do this (which complicates my code) I don't know how to listen for the "setCentralWidget" to avoid trying to connect to empty centralWidget.

    Thank you :)



  • well... didn't you say that connectionwidget is your subclass of QWidget, which is your centralWidget? If you trully are worried about this, you could check to see if QMainWindow::centralWidget() != 0, because "documentation":http://qt-project.org/doc/qt-4.8/qmainwindow.html#centralWidget says that it will return 0 when no widget has been set



  • [quote author="b1gsnak3" date="1365687201"]well... didn't you say that connectionwidget is your subclass of QWidget, which is your centralWidget?[/quote]
    Hey, you're focused, I like it =P

    Yes I did, my ConnectionWidget is a subclass of QWidget, it's intended to be a central widget but not the only one. That's why I'm stuck here. I have to find a way to let children handle the message easily.

    Thanks anyway.



  • Ok. But be careful because setting a new central widget will delete the old one. As to your problem, please, if you can, explain again to me because I can't understand from bits and pieces xD


  • Lifetime Qt Champion

    If you have multiple widgets that will go as "central widget", you might be interested by QStackedWidget. Have all your widgets in the stack and show the right widget at the right time.



  • [quote author="b1gsnak3" date="1366008052"]Ok. But be careful because setting a new central widget will delete the old one.[/quote]

    Yup, thanks. I always @delete this->centralWidget();@


    [quote author="SGaist" date="1366010452"]you might be interested by QStackedWidget.[/quote]
    Unfortunately, I forgot about it and did something ugly, but really efficient for my case.

    I re-explain for b1gsnak3: I want a simple and easy system to change my centralWidget and listen to the child "changeStatus" to route it to statusBar()->showMessage(). I did it my way at the beginning (connection in children, bad), but someone (Objective-C dev) told me about the delegates but I wanted C++ dev advices.

    Here is what I did:

    I've created my own "AbstractWidget" which has a pure virtual slot (nextWidgetAction()), which what the children have to call when they've done their job (Database connection for example).

    "nextWidgetAction()" must actually create a new widget, which is the next widget the parent have to show in its centralWidget, and emit the signal "widgetJobFinished(AbstractWidget *newWidget)".

    The parent is listening to widgetJobFinished() and call setCentralWidget with the parameter.

    I've overloaded "setCentralWidget()" in my MainWindow to take only "AbstractWidget *", to be able to pass it any of my subclassed widgets.

    In "setCentralWidget(AbstractWidget *)", I delete "this->m_centralWidget;" (which is my subclassed current widget).

    Then I call "QMainWindow::setCentralWidget(newWidget);" to set my new centralWidget, store it in my MainWindow and listen to the newWidget's slot "widgetJobFinished(AbstractWidget *)".

    It's not really nice to do that way, but to combine a system where the MainWindow can't know in advance which "type" of widget it has to show when and change the statusBar() correctly, the best way for me was to initialize IN the children the following widget (for i.e.: "welcomeWidget" inits "databaseConnection" which inits "queryWidget") and obviously the widgets can't show backwards (the old are deleted) and there is no need.

    What do you think about that ?


  • Lifetime Qt Champion

    Isn't that some sort of QWizard you are writing ?



  • [quote author="SGaist" date="1366014252"]Isn't that some sort of QWizard you are writing ?[/quote]
    Hum... Now you mention it, yes it is :/
    But I don't need the ability to go backward, but except that, it could be a QWizard.



  • 1st. For your setCentralWidget I would do it like:

    @
    if(this->centralWidget() != 0) delete this->centralWidget();
    @

    2nd. You could've used just one class, with an Enum of properties (which you set when you instantiate the properties) that would be your different types of action your widget needs to do and according to those properties create your widgets and call different methods (or use different constructors for each type of widget). And you would've had one big class instead of multiple ones.



    1. I think you can delete multiple times "NULL" but not an already deleted pointer. So checking if NULL won't guaranty me to delete an allocated space. Anyway, I have the habits to do like this EVERY TIME, to be safe, and to be able to delete it again and again if I try to delete a pointer passed by a parent for example:
      @ptr = new ...;
      delete ptr;
      ptr = NULL;@

    2. I could do like that, but I wanted my MainWindow completely unaware of the children, as a QMainWindow would. So it means, every time I want a new widget, I just have to create it in the previous child and pass it in the SIGNAL. Then I don't need to modify the MainWindow class, and I will have multiple children because anyway, these children will have their own job and so their own class. Isn't it ?


Log in to reply
 

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