Threading in PyQt4

Hello Everyone , I will explain on how to create a rotating circle using the concept of multithreading , qraphicsscene aand qgraphicsview. I struggled a lot to get the syntax( noobish I know) , but this is just to ensure no-one suffers the same.

from PyQt4 import QtGui , QtCore
import sys , time
#this imports the required libraries

We will be inheriting QtGui.QWidget , to create the basic user interface design.

class Example(QtGui.QWidget):
    def __init__(self):
        #Initialising the parent class and calling self.UI
        super(Example , self).__init__()
        self.UI()

My UI function should just have a layout with a graphics view and a button say Run . So this will be my function.

#Inside the UI function.
//Self Explanatory (Creating and adding widgets to my layout)
self.setWindowTitle('Revolving Circle')
self.setGeometry(200 , 200 , 500 , 500)
layout = QtGui.QGridLayout(self)
button = QtGui.QPushButton('Revolve')
view = QtGui.QGraphicsView()
scene = QtGui.QGraphicsScene()
view.setScene(scene)
layout.addWidget(view , 0 , 0)
layout.addWidget(button , 0 ,1)
self.show()
button.clicked.connect(self.revolve) #Connecting the button signal to the function revolve

Now for creating the circle and the line connecting the centre to the circumference of the circle.

#Inside the UI function
circle = QtGui.QGraphicsEllipseItem(150 , 250 , 50 , 50)
scene.addItem(circle)

The parameters that QGraphicsEllipseItem takes? Um. well , assume the ellipse is enclosed in a rectangle , the first two
numbers give the x and y coordinates , of the top left corner of the rectangle , the third and fourth give me the length
of the major axis and minor axis respectively.Now to add the line that represents the radius of the circle.

self.lin = QtCore.QLineF(QtCore.QPointF(175 , 275) , QtCore.QPointF(200 , 275))
self.line1 = QtGui.QGraphicsLineItem.setLine()
self.line1.setLine(self.lin)
scene.addItem(self.line1)

For setting a line in a QGraphicsScene , it is better to have a QLineF set in a QGraphicsLineItem , so that it becomes
easier to update , and the QLineF , takes as parameters , the starting and ending point.

So the basic interface is set. Now to add the function , the button sets off when clicked.

def revolve(self):
    theta = 10
    i = 0
    while 1:
        i = i + 1
        time.sleep(0.01)
        self.lin.setAngle(theta * i)
        self.line1.setLine(self.lin)

This should make the radius revolve right? But it wouldn’t. It would start to hang. The way to rectify is to create
a seperate thread for the time computation and passing it on to a function , instead of letting the GUI function do the
time computation itself. And for people who ask why , theta * i , just theta wouldn’t do because theta is with respect to
the position of the initial line.

So for the threading part , we need to inherit the QtCore.QThread class , and rewrite the run method.

class RevolveThread(QtCore.QThread):
    def __init__(self):
        super(RevolveThread , self).__init__()

    def run(self):
        i = 0
        while 1:
            i = i + 1
            time.sleep(0.01)
            self.emit(QtCore.SIGNAL('update(QString)') + str(i))

So every 0.01 seconds this runs , it emits a signal. which is caught by the function it is connected to.

Now write another function , change , which implements the rotation.

#Inside a function change
self.lin.setAngle(theta * int(i))
self.line1.setLine(self.lin)

Remove other lines in the function revolve , just add lines that initialises the thread , connects it to the function ,
change , and starts the thread

#Inside revolve
thread = RevolveThread()
self.connect(thread , QtCore.SIGNAL('update(QString)') , self.change)
thread.start()

And as for managing threads , its better to create a list of them , and then call them one by one , as and when the
button is clicked.

So when you click on the Revolve Button , you will see a rotating circle.

Reference:
http://joplaete.wordpress.com/2010/07/21/threading-with-pyqt4/

Thanks for reading..

Advertisements

9 comments

  1. Hi, “self.line1 = QtGui.QGraphicsLineItem.setLine(self.lin)”, here you will get an error for unbound method as you haven’t instantiated the QtGui.QGraphicsLineItem. Also , even if you don’t get that exception self.line1 will be return value of setLine method.

    Correct :

    self.line1 = QtGui.QGraphicsLineItem()
    self.line1.setLine(self.lin)

  2. 1. ” thread = WorkThread() ” : There is no “WorkThread()”, it should be RevolveThread()
    2. ” self.emit(QtCore.SIGNAL(‘update(QString)’) ) ” : should have another argument which will return the QString eg. self.emit(QtCore.SIGNAL(‘update(QString)’), str(i))

    Correct me, if I wrong.

    1. All of your comments are right. Editing. Thanks 🙂

  3. Can’t run this.
    Gui loads, but freezes after pressing the Button

    1. Of course I used the Threading version.
      I did something wrong.

  4. avstenit · · Reply

    I am having some problems with running the code as I get an error:
    self.line1 = QtGui.QGraphicsLineItem.setLine()
    TypeError: arguments did not match any overloaded call:
    QGraphicsLineItem.setLine(QLineF): first argument of unbound method must have type ‘QGraphicsLineItem’
    QGraphicsLineItem.setLine(float, float, float, float): first argument of unbound method must have type ‘QGraphicsLineItem’

    I anyone can help me out?

    1. Hi. Its been a long time since I did all this. I’m quite busy right now, I’ll have a look later and I’ll comment

    2. avstenit · · Reply

      Hello! Thanks for quick reply. I found the solution in the first comment from Day Dreamer. But I have some problems understanding about the change function – where to place it and what is inside function change? Can you maybe post the entire code in one part?

  5. from PyQt4 import QtGui,QtCore
    import sys,time

    class Example(QtGui.QWidget):
    def __init__(self):
    super(Example,self).__init__()
    self.UI()
    def UI(self):
    self.setWindowTitle(‘Revolving Circle’)
    self.setGeometry(200,200,500,500)
    layout=QtGui.QGridLayout(self)
    self.button=QtGui.QPushButton(‘Revolve’)
    view=QtGui.QGraphicsView()
    self.scene=QtGui.QGraphicsScene()
    view.setScene(self.scene)
    layout.addWidget(view,0,0)
    layout.addWidget(self.button,0,1)
    self.show()
    self.button.clicked.connect(self.revolve)

    circle=QtGui.QGraphicsEllipseItem(150,250,50,50)
    self.scene.addItem(circle)

    self.lin=QtCore.QLineF(QtCore.QPointF(175,275),QtCore.QPointF(200,275))
    self.line1=QtGui.QGraphicsLineItem()
    self.line1.setLine(self.lin)
    self.scene.addItem(self.line1)
    self.threadpool=[]
    self.thread=RevolveThread()
    def revolve(self):
    self.connect(self.thread,QtCore.SIGNAL(‘update(QString)’),self.change)
    self.thread.start()
    self.thread.started.connect(self.buttexts)
    self.thread.finished.connect(self.buttexte)
    def buttexts(self):
    self.button.setText(“Thread Started”)
    def buttexte(self):
    self.button.setText(“Thread Finished”)
    def change(self,i):
    theta=10
    self.lin.setAngle(theta*int(i))
    self.line1.setLine(self.lin)
    print “degree: “,theta*int(i)
    self.scene.update()
    class RevolveThread(QtCore.QThread):
    def __init__(self):
    super(RevolveThread,self).__init__()
    print “init completed”
    def __del__(self):
    self.wait()
    def run(self):
    print “on the run”
    i=0
    chk=True
    while chk:
    i+=1
    time.sleep(0.03)
    self.emit(QtCore.SIGNAL(‘update(QString)’),str(i))
    if i==200:
    chk=False
    else:
    continue
    app=QtGui.QApplication(sys.argv)
    demo=Example()
    sys.exit(app.exec_())

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: