hey i heard that there is something called qt signals you can use it to change the GUI out side the main thread coz when i try to change it i get an error that i cant change the gui out of the main thread but i need it to edit the progressbar value so if anyone had a way to do it plz tell me

Recommended Answers

All 9 Replies

Member Avatar for nabla2

Hi,

Try something like this:

my_form.py:

# -*- coding: utf-8 -*-

from PyQt4 import QtGui, QtCore

from main_window import Ui_MainWindow
from outside import Outside

class MyForm(QtGui.QMainWindow):

    def __init__(self, parent=None):
        super(MyForm, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        outside = Outside()
        self.connect(outside, QtCore.SIGNAL("progress(int, int)"), self.progress)

    def progress(self, value, max_value):
        self.ui.progressBar.setMinimum(0)
        self.ui.progressBar.setMaximum(max_value)
        self.ui.progressBar.setValue(value)

outside.py:

# -*- coding: utf-8 -*-

from PyQt4 import QtCore

class Outside(QtCore.QThread):

    def __init__(self, parent=None):
        super(Outside, self).__init__(parent)
        self.start()

    def run(self):
        self.emit(QtCore.SIGNAL("progress(int, int)"), i + 1, len(something_iterable))

    def __del__(self):
        self.exiting = True
        self.wait()

why there is

 i + 1, len(something_iterable)

in the line 12 in the out side module?

This code is a simulation (hint) that drives your progress bar for testing purposes..
You have to replace it with code you want to move the progress bar with.

Member Avatar for nabla2

Badly copied piece of code. It should be:

def run(self):
    for i, item in enumerate(something_iterable):
        self.emit(QtCore.SIGNAL("progress(int, int)"), i + 1, len(something_iterable))

yeah but am using python threading module not QtThread
example :

#after importing all modules and set the GUi.....
def progress(self,X):
    self.progress.setProperty("value",x)
def main(self):
    print "value is 0"
    self.progress(0)
    print "now it is 100"
    self.progress(100)
t=threading.Thread(target=main)
t.start()
Member Avatar for nabla2

I propose to use the QThread instead threading.Thread in this case.

It's mostly the same. The main difference is that QThreads are better integrated with Qt (asynchrnous signals/slots, event loop, etc.). Also, you can't use Qt from a Python thread (you can't for instance post event to the main thread through QApplication.postEvent): you need a QThread for that to work.

A general rule of thumb might be to use QThreads if you're going to interact somehow with Qt, and use Python threads otherwise.

Read more

hmmm so it is a deadend....
is there a way to syc a var between a thread and the main GUI thread?
and what about pywx,pygtk,pytk?? can't i change the gui from a thread?

Member Avatar for nabla2

Of course, you can. Read this example carefully. You need a class that inherits from QThread and implements the run method, which is triggered by start(). For communication between the thread and the GUI use the signals and slots.

At first it may be complicated. Writing multithreaded programs requires a bit more knowledge, regardless of whether you are using PyGTK, wxPython, etc.

PyQt is probably the best choice (in my opinion). Slightly modified example (with snippet for more pythonic API 2):

import sys
import math
import random
import sip

API_NAMES = ["QDate", "QDateTime", "QString","QTextStream", "QTime", "QUrl", "QVariant"]
API_VERSION = 2
for name in API_NAMES:
    sip.setapi(name, API_VERSION)

from PyQt4.QtCore import *
from PyQt4.QtGui import *

class Window(QWidget):

    def __init__(self, parent=None):

        QWidget.__init__(self, parent)

        self.thread = Worker()

        label = QLabel("Number of stars:")
        self.spinBox = QSpinBox()
        self.spinBox.setMaximum(10000)
        self.spinBox.setValue(100)
        self.startButton = QPushButton("&Start")
        self.viewer = QLabel()
        self.viewer.setFixedSize(300, 300)

        self.connect(self.thread, SIGNAL("finished()"), self.updateUi)
        self.connect(self.thread, SIGNAL("terminated()"), self.updateUi)
        self.connect(self.thread, SIGNAL("output(QRect, QImage)"), self.addImage)
        self.connect(self.startButton, SIGNAL("clicked()"), self.makePicture)

        layout = QGridLayout()
        layout.addWidget(label, 0, 0)
        layout.addWidget(self.spinBox, 0, 1)
        layout.addWidget(self.startButton, 0, 2)
        layout.addWidget(self.viewer, 1, 0, 1, 3)
        self.setLayout(layout)

        self.setWindowTitle("Simple Threading Example")

    def makePicture(self):

        self.spinBox.setReadOnly(True)
        self.startButton.setEnabled(False)
        pixmap = QPixmap(self.viewer.size())
        pixmap.fill(Qt.black)
        self.viewer.setPixmap(pixmap)
        self.thread.render(self.viewer.size(), self.spinBox.value())

    def addImage(self, rect, image):

        pixmap = self.viewer.pixmap()
        painter = QPainter()
        painter.begin(pixmap)
        painter.drawImage(rect, image)
        painter.end()
        self.viewer.update(rect)

    def updateUi(self):

        self.spinBox.setReadOnly(False)
        self.startButton.setEnabled(True)

class Worker(QThread):

    def __init__(self, parent=None):

        QThread.__init__(self, parent)
        self.exiting = False
        self.size = QSize(0, 0)
        self.stars = 0

        self.path = QPainterPath()
        angle = 2*math.pi/5
        self.outerRadius = 20
        self.innerRadius = 8
        self.path.moveTo(self.outerRadius, 0)
        for step in range(1, 6):
            self.path.lineTo(
                self.innerRadius * math.cos((step - 0.5) * angle),
                self.innerRadius * math.sin((step - 0.5) * angle)
                )
            self.path.lineTo(
                self.outerRadius * math.cos(step * angle),
                self.outerRadius * math.sin(step * angle)
                )
        self.path.closeSubpath()

    def __del__(self):

        self.exiting = True
        self.wait()

    def render(self, size, stars):

        self.size = size
        self.stars = stars
        self.start()

    def run(self):

        # Note: This is never called directly. It is called by Qt once the
        # thread environment has been set up.

        random.seed()
        n = self.stars
        width = self.size.width()
        height = self.size.height()

        while not self.exiting and n > 0:

            image = QImage(self.outerRadius * 2, self.outerRadius * 2,
                           QImage.Format_ARGB32)
            image.fill(qRgba(0, 0, 0, 0))

            x = random.randrange(0, width)
            y = random.randrange(0, height)
            angle = random.randrange(0, 360)
            red = random.randrange(0, 256)
            green = random.randrange(0, 256)
            blue = random.randrange(0, 256)
            alpha = random.randrange(0, 256)

            painter = QPainter()
            painter.begin(image)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setPen(Qt.NoPen)
            painter.setBrush(QColor(red, green, blue, alpha))
            painter.translate(self.outerRadius, self.outerRadius)
            painter.rotate(angle)
            painter.drawPath(self.path)
            painter.end()

            self.emit(SIGNAL("output(QRect, QImage)"),
                      QRect(x - self.outerRadius, y - self.outerRadius,
                            self.outerRadius * 2, self.outerRadius * 2), image)
            n -= 1

if __name__ == "__main__":

    app = QApplication(sys.argv)
    window = Window()
    window.show()
    app.exec_()

hmmmm i guess i need a good tutorial for Qt anyway thanx this question should be marked as solved :)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.