Results 1 to 14 of 14

Thread: statusbar

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Jan 2020
    Posts
    22
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default statusbar

    I am looking for a convenient way to write messages to the statusbar from any widget, which may be instantiated in a subclassed widget, which again is instantiated in a class subclassed from QMainwindow. What is the correct way to do this? I could probably create a chain of getParent() methods, but it seems a bit messy.

  2. #2
    Join Date
    Jan 2006
    Location
    Bremen, Germany
    Posts
    491
    Thanked 73 Times in 69 Posts
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11 Windows

    Default Re: statusbar

    You can create a custom QEvent which can be sent from any widget and be catched by you mainwindow.

  3. The following user says thank you to ChristianEhrlicher for this useful post:

    d_stranz (15th February 2020)

  4. #3
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    4,481
    Thanks
    252
    Thanked 711 Times in 701 Posts
    Qt products
    Qt5
    Platforms
    Windows Android

    Default Re: statusbar

    it seems a bit messy.
    And prone to failure, especially if there is some "hidden" parent inserted by Qt (like a QFrame for example) between some deeply-embedded child and your QMainWindow's status bar.

    Ever hear of "signals and slots"? This is a perfect use case. Implement a slot in your MainWindow to receive status bar messages. Connect signals from your child widgets to this slot.

    Note that you do not need to expose your MainWindow pointer to every child class that sends these signals. There are a couple of solutions:

    1 - Add a signal to each of the classes in between the main window and the child widgets to relay the status update messages. When the main window creates these intermediate it simply connects -their- signal to its update slot. In turn, when each of these intermediate widgets creates its own child widgets, it connects their update signals directly to its update signal. So when a low level widget emits its update signal, it just gets relayed up the chain of signals until it reaches the main window.

    2 - Add a singleton QObject-based class to act as the relay class and expose this as an application-level instance (similar to the way QApplication creates the qApp instance). Your main window class connects to this object's status update signal. In turn, every child widget that wants to send updates can connect to the object's status update slot. The object's status update slot simply emits the status update signal with the contents of the message sent to the slot. The drawback to this is that (a) you need a global variable and (b) all status-sending classes need to know how to retrieve it.

    Note that for option 1 to work, your intermediate classes also do not need to know if the child classes send status update signals. You can use the QMetaObject system to interrogate the child class to see if it has the signal:

    Qt Code:
    1. // In the MyMiddleManWidget constructor:
    2.  
    3. MyChildWidget * pChild = new MyChildWidget( this );
    4.  
    5. QMetaObject * pMeta = pChild->metaObject();
    6. if ( pMeta )
    7. {
    8. // See if it has a "statusUpdate" signal
    9. int sigIndex = pMeta->indexOfSignal( QMetaObject::normalizedSignature( "statusUpdate( QString )" ).constData() );
    10. if ( sigIndex != -1 )
    11. {
    12. // Yes, it does (index > -1), so connect to it to relay it up the parent-child chain
    13. connect( pChild, SIGNAL( statusUpdate( QString ) ), this, SIGNAL( statusUpdate( QString ) ) );
    14. }
    15. }
    To copy to clipboard, switch view to plain text mode 

    The only requirement here is that every status update-sending class has to implement the signal with the identical signature. But it allows you to implement this message-passing system using any classes, and it also allows you to add message sending to -any- child widget later simply by implementing the signal in that child widget. When the parent constructs that child, it will automatically link it into the status update system if it checks for the signal. If there are a lot of middle-man widgets created, you could abstract this into a function that does the check and connect:

    Qt Code:
    1. bool connectToStatusUpdate( QObject * pParent, QObject * pChild )
    2. {
    3. bool bConnected = false;
    4. if ( pParent && pChild )
    5. {
    6. QMetaObject * pChildMeta = pChild->metaObject();
    7. if ( pChildMeta )
    8. {
    9. // See if it has a "statusUpdate" signal
    10. int childSigIndex = pChildMeta->indexOfSignal( QMetaObject::normalizedSignature( "statusUpdate( QString )" ).constData() );
    11. if ( childSigIndex != -1 )
    12. {
    13. // Yes, it does (index > -1), so now check the parent, too
    14. QMetaObject * pParentMeta = pParent->metaObject();
    15. if ( pParentMeta )
    16. {
    17. int parentSigIndex = pParentMeta->indexOfSignal( QMetaObject::normalizedSignature( "statusUpdate( QString )" ).constData() );
    18. if ( parentSigIndex != -1 )
    19. { // Yes!
    20. bConnected = connect( pChild, SIGNAL( statusUpdate( QString ) ), pParent, SIGNAL( statusUpdate( QString ) ) );
    21. }
    22. }
    23. }
    24. }
    25. }
    To copy to clipboard, switch view to plain text mode 

    And of course, you could make this even -more- general for connecting -any- signal-passing network by passing the names of the signals / slots you wish to connect as an argument to connectToStatusUpdate():

    Qt Code:
    1. bool connectSignalChain( QObject * pParent, const QString & parentSignalName, bool bParentSignal, QObject * pChild, const QString & childSignalName, bool bChildSignal );
    To copy to clipboard, switch view to plain text mode 

    where the "bool bParentSignal" and "bool bChildSignal" are used to decide whether you are connecting signal-to-signal or signal-to-slot. E.g. if bParentSignal and bChildSignal are both true, you use the ( pChild, SIGNAL(), pParent, SIGNAL() ) arguments in the connect. If bParentSignal is true and bChildSignal is false, then you use ( pParent, SIGNAL(), pChild, SLOT() ). If pParentSignal is false, but bChildSignal is true, you connect the opposite way: (pChild, SIGNAL(), pParent, SLOT() ). if both pParentSignal and pChildSignal are false, this is an error - you can't have a slot-slot connection.

    You would also have to substitute QMetaObject::indexOfSlot() for QMetaObject::indexOfSignal() depending on the two Booleans.

    Have fun. Betcha this was more than you wanted to know on a Saturday morning.

    Edit: Or this

    You can create a custom QEvent which can be sent from any widget and be catched by you mainwindow.
    Last edited by d_stranz; 15th February 2020 at 18:42.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  5. #4
    Join Date
    Jan 2020
    Posts
    22
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: statusbar

    I put this into the middle class constructor:
    Qt Code:
    1. QMetaObject *pMeta = (QMetaObject*)m_childClass->metaObject();
    2. if ( pMeta )
    3. {
    4. qDebug() << "middleWClass: pMeta";
    5. // See if it has a "statusUpdate" signal
    6. int sigIndex = pMeta->indexOfSignal( QMetaObject::normalizedSignature( "statusUpdate( QString )" ).constData() );
    7. if ( sigIndex != -1 )
    8. {
    9. qDebug() << "middleWidget: sigIndex";
    10. // Yes, it does (index > -1), so connect to it to relay it up the parent-child chain
    11. connect( (QObject*)m_childClass, SIGNAL( statusUpdate( QString ) ),
    12. this, SIGNAL( slotStatusUpdate( QString ) ) );
    13. }
    14. }
    To copy to clipboard, switch view to plain text mode 
    I had to use the (QMetaObject*)m_childClass cast in order to get it to compile.

    In my child class I emit a signal:
    Qt Code:
    1. void childClass::statusUpdate(QString message)
    2. {
    3. qDebug() << "childClass::statusUpdate" << message;
    4. }
    5.  
    6. void childClass::setStatusbar(QString message)
    7. {
    8. qDebug() << "childClass::slotSetStatusbar()";
    9.  
    10. emit statusUpdate(message);
    11. }
    To copy to clipboard, switch view to plain text mode 
    The debug message "childClass::slotSetStatusbar()" is duly output.

    The first middle class debugg message "middleWidget: pMeta" is output, but not the second one: "middleWidget: sigIndex". So it seems to be something wrong with the line:
    Qt Code:
    1. int sigIndex = pMeta->indexOfSignal( QMetaObject::normalizedSignature( "statusUpdate( QString )" ).constData() );
    To copy to clipboard, switch view to plain text mode 
    Or is it something wrong with the way I emit the signal?

  6. #5
    Join Date
    Mar 2009
    Location
    Brisbane, Australia
    Posts
    7,620
    Thanks
    13
    Thanked 1,592 Times in 1,520 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows
    Wiki edits
    17

    Default Re: statusbar

    C-style cast is evil ... and also unnecessary here:
    Qt Code:
    1. QMetaObject *pMeta = (QMetaObject*)m_childClass->metaObject();
    To copy to clipboard, switch view to plain text mode 
    That function returns QMetaObject* so the C-style cast is not needed. You may need:
    Qt Code:
    1. const QMetaObject *pMeta = ...
    To copy to clipboard, switch view to plain text mode 
    The code above will not compile unless m_childClass is a QObject* with a metaObject() function, so the cast in the following code is not needed either.
    Qt Code:
    1. connect( (QObject*)m_childClass, SIGNAL( statusUpdate( QString ) ),
    2. this, SIGNAL( slotStatusUpdate( QString ) ) );
    To copy to clipboard, switch view to plain text mode 
    Your code implies a slot inside the SIGNAL() macro.
    The child widget's statusUpdate() signal should be connected to the intermediate object's (this) statusUpdate() signal.
    Connecting signal A to signal B just causes signal B to be sent when signal A is emitted. This is how the relay up the tree works.
    Only at the topmost level is the child signal connected to a slot that actually updates the status bar.

  7. #6
    Join Date
    Jan 2020
    Posts
    22
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: statusbar

    I changed the code to
    Qt Code:
    1. const QMetaObject *pMeta = ...
    To copy to clipboard, switch view to plain text mode 
    and it compiles and seems to work, in the sense that my first debug message is output. However, when I remove the cast on
    Qt Code:
    1. connect( (QObject*)m_childClass, SIGNAL( statusUpdate( QString ) ),
    2. this, SIGNAL( slotStatusUpdate( QString ) ) );
    To copy to clipboard, switch view to plain text mode 
    cpp complains, with the error message:
    ... error: ‘QObject’ is an inaccessible base of ‘m_childClass’
    this, SIGNAL( slotStatusUpdate( QString ) ) );

    You whote: "The code above will not compile unless m_childClass is a QObject* with a metaObject() function". m_childClass starts like this:
    Qt Code:
    1. class m_childClass : public QWidget
    2. {
    3. Q_OBJECT
    4. .
    5. .
    To copy to clipboard, switch view to plain text mode 
    Doesn't that imply that is has a QObject* with a metaObject() function?

  8. #7
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    4,481
    Thanks
    252
    Thanked 711 Times in 701 Posts
    Qt products
    Qt5
    Platforms
    Windows Android

    Default Re: statusbar

    class m_childClass : public QWidget
    This says that "m_childClass" is the name of a C++ class, not a pointer to an instance of a class.

    connect( (QObject*)m_childClass, SIGNAL( statusUpdate( QString ) ), this, SIGNAL( slotStatusUpdate( QString ) ) );
    So this code can't possibly work. The connect() method uses pointers to classes, not class names. This probably explains why the compiler is complaining about your code and why you are having to go through all these cast contortions. And as ChrisW67 says, C-style casts like (QObject*) are evil and usually indicate you are doing something wrong if your code won't compile without them.

    Go back and read the code in my original post again and look for the differences between class names and class instances and compare to your code.

    void childClass::statusUpdate(QString message)
    {
    qDebug() << "childClass::statusUpdate" << message;
    }
    This cannot be a signal. You declare a signal in the "signals:" section of your class definition and then must not write any code in your cpp file. The Qt metacompiler will read your header file and create the code for the signal in the uic file that gets generated. The C++ compiler and linker then compile the uic file and link it in to your program. This probably explains why the indexOfSignal() method is returning a -1, because you have not defined the signal correctly.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  9. #8
    Join Date
    Jan 2020
    Posts
    22
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: statusbar

    Quote Originally Posted by d_stranz View Post
    This says that "m_childClass" is the name of a C++ class, not a pointer to an instance of a class.
    This was a typing error; what I had was;
    Qt Code:
    1. class childClass : QWidget
    To copy to clipboard, switch view to plain text mode 
    However, I had forgotten "public" in front of QWidget, and that was why it didn't compile.

    Quote Originally Posted by d_stranz View Post
    And as ChrisW67 says, C-style casts like (QObject*) are evil ...
    Yes, I agree. I should have used a static_cast, if I needed one. But now I don't anymore.
    Quote Originally Posted by d_stranz View Post
    This cannot be a signal. You declare a signal in the "signals:" section of your class definition and then must not write any code in your cpp file.
    That was another major error.

    I got it working in the end, using this rather simple connection:
    Qt Code:
    1. connect( m_childClass, SIGNAL( valueChanged( QString ) ),
    2. this, SLOT( slotStatusUpdate( QString ) ) );
    To copy to clipboard, switch view to plain text mode 
    in the class above the child class. Now that it works I am happy, but what would the advantage be of your code, starting with;
    Qt Code:
    1. QMetaObject *pMeta = (QMetaObject*)m_childClass->metaObject();
    To copy to clipboard, switch view to plain text mode 
    To me it seems a bit complicated, but I presume there will be some advantages to it? Is it safer?

    My child class now inherits from QObejct:
    Qt Code:
    1. class childClass : public QObject
    To copy to clipboard, switch view to plain text mode 
    but I presume it could just as well have inherited from QWidget (which inherits from QObject):
    Qt Code:
    1. class childClass : public QWidget
    To copy to clipboard, switch view to plain text mode 
    Are there any advantages of inheriting from QObject instead of QWidget, when the child class isn't part of the user interface?

  10. #9
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    4,481
    Thanks
    252
    Thanked 711 Times in 701 Posts
    Qt products
    Qt5
    Platforms
    Windows Android

    Default Re: statusbar

    I should have used a static_cast, if I needed one.
    No, static_cast< Foo * > is exactly the same as (Foo *), and says "Treat this pointer as a pointer to a Foo, no matter what it really points to." If it turns out that the pointer actually points to a Bar, and the Bar class is completely unrelated to a Foo class, then at best your code will crash and worse it could cause an error somewhere else down the line because using a Foo that isn't really a Foo could corrupt memory. If you have to cast at all, you should be using dynamic_cast< Foo * > if Foo is an ordinary C++ class, or qobject_cast< Foo * > if Foo is derived from QObject. Either of these will return a NULL pointer if the object instance being cast cannot be cast to an object instance of the target type. static_cast<> will always succeed, which can lead you down a very dangerous road.

    QMetaObject *pMeta = (QMetaObject*)m_childClass->metaObject();
    As ChrisW67 said, the cast is unnecessary, since QObject::metaObject() already returns a QMetaObject pointer.

    Are there any advantages of inheriting from QObject instead of QWidget, when the child class isn't part of the user interface?
    In order to emit signals, a class must derive from QObject (and include the Q_OBJECT macro in its definition). If a class is not a UI component, then it makes no sense to derive from QWidget. You aren't using any of the QWidget properties (ie. things related to display on screen).

    To me it seems a bit complicated, but I presume there will be some advantages to it?
    What I was proposing to you was a general mechanism for relaying messages from some child widget deep in your UI up to the main window for display in the status bar. The first version required that every parent widget must implement a "statusUpdate" signal (it can be named anything you want). In addition, any child widget that wants to emit status messages must also have a "statusUpdate" signal. When the constructor adds a new child widget (of any type), it checks to see if the child widget has a "statusUpdate" signal. If it does, it adds it to the relay chain by connecting the child's signal ot its own signal. When the child emits the statusUpdate signal, this causes the parent to emit its signal with the same message, thus relaying it up the chain until it gets to the main window.

    The benefit of this is that you can have a mix of child widgets that do and do not emit statusUpdate messages. If you implement a child widget that does not generate status methods, you can later add the ability to generate such messages but the parent class does not need to be modified. If you implement the code to check for the signal for each child class you add, when the child class interface changes to add statusUpdate signals, the parent code will automatically link it into the relay chain.

    The second, more general method I wrote was exactly that - a completely general way to link any two QObject instances into a signal passing chain. If the parent object has a signal or slot with the parent signature passed in, and the child has a signal or slot with the child signature passed in, the code will create a connection between the two. It has the advantage that you can connect any two object instances together - they don't have to be in a direct parent - child arrangement, and they don't have to be QWidget instances but can be anything derived from QObject.

    Like I said, it's probably way more than you need, but it hopefully it got you thinking about how to exploit some of the less well-known features of Qt.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  11. #10
    Join Date
    Jan 2020
    Posts
    22
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: statusbar

    I kind of see now that your (complicated-looking) solution can be useful, although for the time being I will try to keep things as simple as possible. But I will keep your solution in mind for future use. Anyway, your comments were helpful for understanding a bit more of Qt's message system. Thank you.

  12. #11
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    4,481
    Thanks
    252
    Thanked 711 Times in 701 Posts
    Qt products
    Qt5
    Platforms
    Windows Android

    Default Re: statusbar

    Thinking it out was useful for me too and I learned a few things about QMetaObject. I might try to find a use for these ideas n my own projects.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  13. #12
    Join Date
    Jan 2006
    Location
    Bremen, Germany
    Posts
    491
    Thanked 73 Times in 69 Posts
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11 Windows

    Default Re: statusbar

    Quote Originally Posted by d_stranz View Post
    No, static_cast< Foo * > is exactly the same as (Foo *), and says "Treat this pointer as a pointer to a Foo, no matter what it really points to." If it turns out that the pointer actually points to a Bar, and the Bar class is completely unrelated to a Foo class
    I'm sorry but this is wrong. You mixed it up with reinterpret_cast

  14. The following user says thank you to ChristianEhrlicher for this useful post:

    d_stranz (19th February 2020)

  15. #13
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    4,481
    Thanks
    252
    Thanked 711 Times in 701 Posts
    Qt products
    Qt5
    Platforms
    Windows Android

    Default Re: statusbar

    I'm sorry but this is wrong. You mixed it up with reinterpret_cast
    Yes, thanks, you're correct as usual. I so rarely use casting, usually only in the case where I am passed a base class pointer or reference in a virtual function and it must be cast to a derived class for use. Otherwise I stay away from it.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  16. #14
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    4,481
    Thanks
    252
    Thanked 711 Times in 701 Posts
    Qt products
    Qt5
    Platforms
    Windows Android

    Default Re: statusbar

    Actually, on researching this, static_cast<> and reinterpret_cast<> seem to be essentially identical in their effect, the difference is when the cast occurs. static_cast<> occurs at run time, whereas reinterpret_cast<> is a compiler directive. See this.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

Similar Threads

  1. Writing to StatusBar
    By snowsoman in forum Qt Programming
    Replies: 1
    Last Post: 23rd November 2011, 16:50
  2. Frame around whole statusbar
    By binaural in forum Qt Programming
    Replies: 1
    Last Post: 22nd April 2010, 18:14
  3. StatusBar Message
    By chethana in forum Qt Programming
    Replies: 1
    Last Post: 2nd June 2009, 15:44
  4. statusbar
    By bhogasena in forum Qt Programming
    Replies: 7
    Last Post: 3rd February 2009, 14:54
  5. problem with statusbar
    By philipp1 in forum Qt Programming
    Replies: 1
    Last Post: 13th October 2006, 07:12

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.