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

Qt for Python Quick Start tutorial refresh bug?



  • https://doc.qt.io/qtforpython/quickstart.html

    The "Hello World" tutorial coded here does not refresh after clicking the button.
    It worked properly by adding the line

    self.text.repaint()

    as part of def magic(self):

    I am completely new to everything (programming, python and qt), so it took me a while to come up with this solution. Should it have worked as written (I tested it with PyCharm)? If not, is this the optimal solution, or did I miss something better in the docs?


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    What version of PySide2 are you using ?
    On what platform ?
    How did you install it ?



  • @tomtill said in Qt for Python Quick Start tutorial refresh bug?:

    self.text.repaint()

    You absolutely should not have to do that! I haven't tried the code in the example, but have looked at it, and it should be fine as it is. I don't know what to say to you other than try it again, and make sure you have the code as shown there....



  • @SGaist, @JonB
    I'm using PySide2 5.14.1 on MacOS 10.14.6 (18G3020)
    I installed Pyside2 using pip install Pyside2

    I copied and pasted the code, and ran it using Python 3.8.1, Qt Creator 14.11.0, PyCharm CE (2019.3.3), and from the terminal, with identical results from each. From my limited understanding, I wouldn't expect different behavior since they all refer to the same virtual environment running the same Python installation.

    The window comes up with Hello World and the button, but when I click the button nothing happens. If I resize the window, the language of Hello World changes, which should have happened after I pressed the button. Adding the repaint() gives me the expected behavior, but I haven't seen that function used in any tutorial with Qt for Python.

    Here is the copy/pasted script:

    import sys
    import random
    from PySide2 import QtCore, QtWidgets, QtGui

    class MyWidget(QtWidgets.QWidget):
    def init(self):
    super().init()

        self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"]
    
        self.button = QtWidgets.QPushButton("Click me!")
        self.text = QtWidgets.QLabel("Hello World")
        self.text.setAlignment(QtCore.Qt.AlignCenter)
    
        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.text)
        self.layout.addWidget(self.button)
        self.setLayout(self.layout)
    
        self.button.clicked.connect(self.magic)
    
    def magic(self):
        self.text.setText(random.choice(self.hello))
    

    if name == "main":
    app = QtWidgets.QApplication([])
    widget = MyWidget()
    widget.resize(800, 600)
    widget.show()
    sys.exit(app.exec_())



  • @tomtill
    Firstly, please learn to post code here properly protected/formatted. Place a line with 3 adjacent backquotes at the start and at the end. Because you have not done so, you can see your code is missing all its __ (double-underscore) sequences (they show as bold text!), so it won't work for anyone copy/pasting.

    I have pasted the following code:

    import sys
    import random
    from PySide2 import QtCore, QtWidgets, QtGui
    
    class MyWidget(QtWidgets.QWidget):
      def __init__(self):
        super().__init__()
    
        self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"]
    
        self.button = QtWidgets.QPushButton("Click me!")
        self.text = QtWidgets.QLabel("Hello World")
        self.text.setAlignment(QtCore.Qt.AlignCenter)
    
        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.text)
        self.layout.addWidget(self.button)
        self.setLayout(self.layout)
    
        self.button.clicked.connect(self.magic)
    
      def magic(self):
        self.text.setText(random.choice(self.hello))
    
    if __name__ == "__main__":
      app = QtWidgets.QApplication([])
      widget = MyWidget()
      widget.resize(800, 600)
      widget.show()
      sys.exit(app.exec_())
    

    I confirm that this does work, updating the label text as soon as the button is clicked. I have tested under Ubuntu 19.04 with both PySide2 & PyQt5. Python is 3.7.3, Qt/PySide2/PyQt5 is 5.12.2, as supplied with the Ubuntu release.

    I simply do not know why your mileage varies, and you seem to have to force a repaint (one way or another) for it to appear. Maybe @SGaist has some clue....



  • @JonB
    Thank you for your instruction, and your efforts to reproduce my finding.
    I will triple backquote any code I paste here.

    I copy and pasted your code into my PyCharm, and observed the same refresh error I saw before.
    So it does not appear to be something subtle in my code.

    From your result, this appears to be MacOS thing, either a Qt bug, a Python bug, or something in my particular setup. I installed the MacOS binaries of both Python and Qt from their respective websites. I cherry picked the Qt installation, since I wished to avoid anything that was not LGPL ( I am a hobbyist, but one can always dream…). Might I have missed an essential component? Should I have built from source?

    Is this likely to be a problem in an actual app? A more complicated (useful) app would have a lot more going on which would likely force a refresh anyway, no?



  • @tomtill
    It is not your code. You are doing nothing wrong. A more complex example would likely reproduce the issue, and in any case this issue just as exemplified in your code for you must be addressed.

    You have not likely done anything wrong in whatever you have installed. If you had missed something, you would not get this behaviour. But some subtle incompatibility is always possible.

    If you are offering to try to discover more: I would completely uninstall whatever Qt you have. Then, for choice, I would pick a different (older) version to restore, e.g. even back to the 5.12.2 I use. If it happens there too something is very weird. In a miracle world, starting again with uninstall & reinstall may even make it go away, by magic.


  • Moderators

    @tomtill
    my guess would be your rather old MacOS version in combination with a new Qt Version is the issue here.

    Mojave changed the update/repaint signal/event handling, which caused major problems
    see here:
    https://bugreports.qt.io/browse/QTBUG-68891?focusedCommentId=410788&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel

    TL;DR: the DarkMode is to blame.

    It should work fine, when you use 5.14.1 like you said you do, but one never knows.

    I would suggest updating MacOS or using an LTS version of Qt 5.12.x or 5.9.x


  • Banned

    Okay I am not sure why your version does not work because I did not change much of your functionality just cleaned it up a bit and made it safer and a bit more proper python-qt --- I hope this example with its explanations helps though. Also some of the changes are pure preference or style but I included them because I feel they help make the code more easily readable which is always important

    from PySide2.QtCore    import Qt, Slot
    from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout
    from PySide2.QtWidgets import QPushButton, QLabel
    
    # Always declare other imports after your Qt imports
    # import sys <-- no longer needed
    from random import choice as rdmChoice
    
    class MyWidget(QWidget):
        def __init__(self):
          # Do not use super( ) unless you are fully aware of what 3 major issues you
          # must code for in conjunction with using it and the actual rare issue that
          # it is meant to solve. Note this rare issue is rare and unless you are doing
          # some complicated inheritance you will most likely never run into that
          # issue -- however the 3 major issues it creates by using you are more 
          # likely to run into than the rare issue its meant for
            QWidget.__init__(self)
            self.setWindowTitle('Hello')
            Width = 250; Hight = 100
            self.resize(Width, Hight)
    
            self.hello = ['Hallo Welt', 'Hei maailma', 'Hola Mundo', 'Привет мир', 'Hello World']
    
            self.btnClick = QPushButton('Click me!')
          # Note this is a Signal and the function it delcares is a Slot
            self.btnClick.clicked.connect(self.Magic)
    
            self.txtHolder = QLabel('Hello World')
            self.txtHolder.setAlignment(Qt.AlignCenter)
    
          # A layout does not need self unless you plan to reference
          # after using it here which is not the case here
            VBox = QVBoxLayout()
            VBox.addWidget(self.txtHolder)
            VBox.addWidget(self.btnClick)
            VBox.addStretch(1)
    
            self.setLayout(VBox)
    
      # While you can get away sometimes not using these sometimes they can act
      # a bit querky so it always best to use the decorator when using a Slot
        @Slot()
        def Magic(self):
            ChosnOne = rdmChoice(self.hello)
          # Placed to validate it is happening as expected
            print('Chosen One :',ChosnOne)
            self.txtHolder.setText(ChosnOne)
      
    if __name__ == '__main__':
        MainEvntHndlr = QApplication([])
    
        MainApp = MyWidget()
        MainApp.show()
    
      # This is PyQt4 and while still backwards compatible eventually it will not be
      # sys.exit(app.exec_())
      # This is the current PyQt5 way of doing it
        MainEvntHndlr.exec()
    
      # If anyone wants more extensive free help I run an online lab-like classroom-like 
      # message server feel free and drop by you will not be able to post until I clear 
      # you as a student as this prevents spammers so if interested here is the invite
      # https://discord.gg/3D8huKC
    


  • @JonB
    Of course you are right. Different behavior of the same code on different platforms is surely a bug, not a feature.

    On that note, do you think that this may be an issue specifically with Pyside2?

    I could install PyQt5 in a different virtual environment. If that works, then it would define a bug in Pyside2, no? (I just have to figure out how the code should differ between Pyside2 and PyQt.)



  • @J-Hilk
    It looks like this is an open bug report, PYSIDE-695, and has been confirmed by multiple people using Catalina as well as Mojave and various versions of PySide2.

    https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-695?filter=allopenissues

    Apparently it's been around for a while.



  • @Denni-0
    Thank you! I enjoyed your rewrite of the code...learning a lot from this sort of thing.

    Your code works (after adding an underscore after exec) sort of, even though I've tracked down the issue to a previously reported bug in PySide2:

    https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-695?filter=allopenissues

    Comparing your code to the previous code, I was able to isolate a single line that makes the difference. Adding self.layout.addStretch() to the original code gives the desired refresh (although the message is no longer centered). Commenting out "VBox.addStretch(1)" in your code gives a partial refresh if the window is small, but no refresh once the window is enlarged.

    Following a comment made by @alexheinz on the bug report, I can get the desired behavior by adding the line "app.setStyle('Fusion')" in the original code, or "MainEvntHndlr.setStyle('Fusion') " in your code. This also works with "setStyle('Windows')". Apparently the default style (Macintosh) in MacOS is the only one with an issue.


  • Banned

    Okay these must be specific issues with PySide2 because it does not occur in PyQt nor in Qt

    Next perhaps PySide2 is really way behind the 8-ball because .exec() is the Qt5 way of doing this and .exec_() is the Qt4 way of doing this

    Also the .addStretch(1) should not actually affect the refresh at all it only controls how the refresh occurs -- so some other underlying issue with PySide2 is causing this so unless you need the extra "L" attached to the GPL I would go with PyQt5



  • I can work around the Pyside2 MacOS refresh bug using app.setStyle('Fusion').
    Maybe they'll even fix the bug before I am ready to deploy.
    Thank you all for your help figuring this out. I am marking this Topic solved.


Log in to reply