user1965493
user1965493

Reputation: 47

Problems with the order execution functions when use PyQT4

I have a problem with PyQT4 for Python. There is a label with text and button connected to function. The function could change the text of label first, then call other function. There is a problem with it: the function is executed firts, then change text of the label.

Code:

# -*- coding: utf-8 -*-
import time
import sys
from PyQt4 import QtCore, QtGui

def timesleep():
    print("start sleep")
    time.sleep(5)
    print("stop sleep")



class AnyWidget(QtGui.QWidget):
    def __init__(self,*args):

        QtGui.QWidget.__init__(self,*args)
        self.setWindowTitle("PETHARD")
        boxlay = QtGui.QHBoxLayout(self)
        frame = QtGui.QFrame(self) # Фрейм
        frame.setFrameShape(QtGui.QFrame.StyledPanel)
        frame.setFrameShadow(QtGui.QFrame.Raised)

        gridlay = QtGui.QGridLayout(frame) # Менеджер размещения элементов во фрейме
        label = QtGui.QLabel(u"Welcome",frame) # Текстовая метка.
        global glabel
        glabel = label
        gridlay.addWidget(label,0,0)
        button1 = QtGui.QPushButton(u"Load From MC", frame)
        self.connect(button1, QtCore.SIGNAL("clicked()"), self.ts)
        gridlay.addWidget(button1,1,0)
        boxlay.addWidget(frame)

    def ts(self):
        global glabel
        glabel.setText(u"Waiting...")
        timesleep()

if __name__=="__main__":
    app = QtGui.QApplication(sys.argv)
    aw = AnyWidget()
    aw.show()
    sys.exit(app.exec_())

Help me please to fix this problem.

Upvotes: 0

Views: 277

Answers (3)

unutbu
unutbu

Reputation: 879591

You never want to tie-up your GUI with a long-running function. That the label does not update is only one manifestation of the problem. Even if you get the label to update before the function is called, it will still "freeze" your GUI -- no widget will respond to the user until the long-running function completes.

If you have to run such a function, see if there is a way to break it up into small pieces which each relinquish control to the GUI, or consider using a separate thread to run the function:

import time
import sys
from PyQt4 import QtCore, QtGui


class TimeSleep(QtCore.QThread):
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.parent = parent

    def run(self):
        print("start sleep")
        time.sleep(5)
        print("stop sleep")
        self.parent.glabel.setText(u"Done")


class AnyWidget(QtGui.QWidget):
    def __init__(self, *args):

        QtGui.QWidget.__init__(self, *args)
        self.setWindowTitle("PETHARD")
        boxlay = QtGui.QHBoxLayout(self)
        frame = QtGui.QFrame(self)  # Фрейм
        frame.setFrameShape(QtGui.QFrame.StyledPanel)
        frame.setFrameShadow(QtGui.QFrame.Raised)

        gridlay = QtGui.QGridLayout(
            frame)  # Менеджер размещения элементов во фрейме
        label = QtGui.QLabel(u"Welcome", frame)  # Текстовая метка.
        self.glabel = label
        gridlay.addWidget(label, 0, 0)
        button1 = QtGui.QPushButton(u"Load From MC", frame)
        self.connect(button1, QtCore.SIGNAL("clicked()"), self.ts)
        gridlay.addWidget(button1, 1, 0)
        boxlay.addWidget(frame)

    def ts(self):
        self.glabel.setText(u"Waiting...")
        self.thread = TimeSleep(parent=self)
        self.thread.start()


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    aw = AnyWidget()
    aw.show()
    sys.exit(app.exec_())

Here is a wiki page on how to deal with long-running functions.

Upvotes: 1

ecatmur
ecatmur

Reputation: 157354

The text of the label is being changed, but because you're blocking the main thread you're not giving Qt a chance to paint it.

Use QCoreApplication::processEvents and QCoreApplication::flush:

def ts(self):
    glabel.setText(u"Waiting...")
    QtCore.QCoreApplication.processEvents()
    QtCore.QCoreApplication.flush()
    timesleep()

Upvotes: 1

Rafał Łużyński
Rafał Łużyński

Reputation: 7312

It work's like that because rendering is done later in app. So your glabel.text is changed immediately but you will see changed text on screen after your ts function call, because drawing is done at the end of loop.

If you really want to call your function in new frame (after rendering of new text) then use timer:

timer = QtCore.QTimer()
QtCore.QObject.connect(
    timer,
    QtCore.SIGNAL("timeout()"),
    self.ts
)
timer.start(10)

It should call your function ten milisecond later, so in fact probably after rendering.

Upvotes: 2

Related Questions