Minimal requirement to update a QLabel's text and show it
-
Hi,
A QProgressBar or a QProgressDialog ? Because the later uses processEvent if modal. See the value property documentation.
-
Hi, what about QProgressBar::setFormat() (http://doc.qt.io/qt-5/qprogressbar.html#format-prop) - you can use it to display text on top of the progressbar. This is not at the left of the progressbar, but it might adress your issue as well.
-
Hi,
A QProgressBar or a QProgressDialog ? Because the later uses processEvent if modal. See the value property documentation.
@SGaist
I am using aQProgressBar
as a widget, at the bottom of an overall page (QWidget
). It is not some kind ofQProgressDialog
. But that should be neither here nor there.Forget I ever mentioned
QProgressBar
. This is a question about when an update to the text of aQLabel
is supposed to actually be "visible" to the user, when the code doesQLabel.setText()
s dynamically as it goes along doing something and it does not callQApplication::processEvents()
(or hit the event loop). So the widget does not get a repaint event (right?). In that situation, what do I have to do to get the widget to show its new text now to the user? I am finding I must callQApplication::processEvents()
for the new text to show, do I have to call that? -
If you have a loop or something that does block the event loop, then yes, you have to call processEvents otherwise the paint request queued won't be handled by the event loop.
-
If you have a loop or something that does block the event loop, then yes, you have to call processEvents otherwise the paint request queued won't be handled by the event loop.
@SGaist
Thanks, that is indeed how I understood it. And there isn't a way to just cause paint request(s) to be processed? To force an immediate update but not other things? I seeQEventLoop::ExcludeUserInputEvents
, possible, I had in mind paints and only paints? All I want to do is force this text and only this text to update right now, nothing else, please! -
One thing you can try is to add an event filter to your QLabel and ignore everything that's not a paint event.
-
One thing you can try is to add an event filter to your QLabel and ignore everything that's not a paint event.
@SGaist
Yes, butQApplication::processEvents()
sprays events all over the place, I'm more worried about what everything else might do than I am about my own widget! I just want my widget to update, or at least nothing but paint events everywhere, period! But I guess you've answered.Let me ask one question though: a
QProgressBar
manages to update its own text/visuals (when I goQProgressBar::setValue()
) without doing aQApplication::processEvents()
. How does it do that? -
If you don't want any event to be handle by the QApplication instance, then you have to do the same things a QProgressDialog: spin your own event loop.
-
If you don't want any event to be handle by the QApplication instance, then you have to do the same things a QProgressDialog: spin your own event loop.
@SGaist , or whoever,
OK, there is something "magic" about
QProgressBar
which differs fromQLabel
, and I need someone to explain what it is, please. Basically, aQProgressBar
updates visually without my needing to callQApplication::processEvents()
but aQLabel
does not.Consider the following sample, skeleton code:
Main program:if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) main = Main() sys.exit(app.exec_())
Main function:
class Main(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Main") self.setGeometry(100, 100, 500, 500) self.centralWidget = QtWidgets.QWidget(self) self.setCentralWidget(self.centralWidget) self.centralLayout = QtWidgets.QHBoxLayout(self.centralWidget) self.lblPb = QtWidgets.QLabel() self.centralLayout.addWidget(self.lblPb) self.lblPb.setText("Initial") self.pb = QtWidgets.QProgressBar() self.centralLayout.addWidget(self.pb) self.pb.setRange(0, 10) self.pb.setValue(0) self.show() QtCore.QTimer.singleShot(1000, self.doProgress)
doProgress() function:
def doProgress(self): import time for i in range(10 + 1): # update the label text self.lblPb.setText(str(i)) # We need *one* of the next two lines # to see the updated label text from above self.pb.setValue(i) # QApplication::processEvents() # Yes, the next line is *deliberately* a "sleep" # It *simulates* doing some processing but *not* calling `processEvents()` time.sleep(1)
So, I'm just setting off to call
doProgress()
after the main window is up & shown. Its job is to update theQProgressBar
and theQLabel
while in a non-event-processing-loop, to see what is visible.Now, what is interesting is that the progressive
QLabel.setText()
is only visible because we are also callingQProgressBar.setValue()
. The progress bar gets visually updated, and so does the label text. But if we remove the call toQProgressBar.setValue()
we do not see the label updating. We then have to put in an explicitQApplication::processEvents()
to get to see the label's new value.So: what is
QProgressBar.setValue()
doing exactly to cause this, and where is that documented, please, please? -
Ah ha!!!
Googling around, I have come across
QWidget::repaint()
. After callingQLabel::setText()
, I can just callQLabel::repaint()
and I see the text immediately updated, just as wanted, without having to callQApplication::processEvents()
orQProgressBar.setValue()
.I'd still like to know what
QProgressBar.setValue()
does to allow theQLabel
to repaint too, but I seem to have my solution.May I ask: I kept asking "how can I force widget repaint without processing events", and nobody drew my attention to
QWidget::repaint()
. Why is that, please? -
Nothing against you, I worked from memory and I had forgotten about the specifics of repaint well update VS repaint.
What QProgressBar does is that it check if it needs to repaint based on internal logic and calls repaint if it turns out to be the case.
Just in case, don't forget the warning and recommendation in the repaint documentation.
Thanks for the reminder :)
-
Nothing against you, I worked from memory and I had forgotten about the specifics of repaint well update VS repaint.
What QProgressBar does is that it check if it needs to repaint based on internal logic and calls repaint if it turns out to be the case.
Just in case, don't forget the warning and recommendation in the repaint documentation.
Thanks for the reminder :)
@SGaist
Thanks, no problem. I trust you to be a guru :) I just had to understand why I was hitting my head against a brick wall, I believed there just had to be some kind of "repaint now, don't wait for event loop".It seems to be ideal for my "force update of text while doing other stuff". Just like the docs say, and yes I am aware of recursion and no I won't be inside another
paintEvent
!For the record,
QProgressBar.setValue()
must do more than just "repaint itself" (e.g. by callingrepaint()
). My point is, when I callQProgressBar.setValue()
in addition to seeing the progressbar repaint I also see the unrelatedQLabel
repaint. So it must actually/presumably be doing some kind ofprocessEvents()
, allowing other widgets to paint etc. too. It's easy for me to say this, but I'd really like to see thesetValue()
docs have something to say about this immediate repaint/process events behaviour, as it's "unique" toQProgressBar
and can be very relevant... (I know, I know, I need to start getting into raising Qt docs requests!)Meanwhile I've got what I need, and I (more or less) understand what's going on, so thank you.
-
It doesn't, widgets are only responsible for dealing with their own updates, as for what happens to their parents, it's not them to decide. You can see the setValue implementation here.
What you can also do is submit fixes for the documentation to make it clearer :)
-
It doesn't, widgets are only responsible for dealing with their own updates, as for what happens to their parents, it's not them to decide. You can see the setValue implementation here.
What you can also do is submit fixes for the documentation to make it clearer :)
It doesn't
Now we have a problem! Because I have posted code above which demonstrates that calling
QProgressBar.setValue()
not only paints itself but also causes my unrelatedQLabel
to get repainted. If I take out theQProgressBar.setValue()
then I have to call eitherQApplication::processEvents()
orQLabel::repaint()
to see the altered text in theQLabel
. So how do you explain that?? :) I agree I can't see much in theQProgressBar.setValue()
, but I've given the code above from a test I did, so what's causing theQLabel
to repaint when all I do is callQProgressBar.setValue()
?! -
My point was that QProgressBar doesn't do anything special with regard to other widgets surrounding it (as you saw from its implementation). It's a border effect, nice in your case, but I wouldn't rely on it.
-
My point was that QProgressBar doesn't do anything special with regard to other widgets surrounding it (as you saw from its implementation). It's a border effect, nice in your case, but I wouldn't rely on it.
@SGaist said in Minimal requirement to update a QLabel's text and show it:
It's a border effect
What does that mean? Are you saying something like: the
QProgressBar::repaint()
call (inQProgressBar.setValue()
) just happens to cause theQLabel
to repaint because the widgets are close to each other, there is some overlap? -
From a quick look, it goes down to the rendering engine, I haven't read the whole implementation to determine exactly how the repainting of one of the widget triggers an update of the whole "container" widget.