0

I used pyqt to build a little gui where you can choose a Key from a QComboBox and the Value is taken to do a math calculation that usually takes 3 seconds and the result is a short string. I'm updating the calculated short string in the gui in a textbox. This is my code (I removed the math calculation code because it's not relevant), it is working so far:

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import os 
dict1 = {"Key":Value, "Key2":Value2, "Key3":Value3} # Value are int's

class combodemo(QWidget):
  def __init__(self, parent = None):
    super(combodemo, self).__init__(parent)

    layout = QHBoxLayout()
    self.cb = QComboBox()
    self.cb.addItems([key for key in sorted(dict1.keys())])
    self.cb.currentIndexChanged.connect(self.selectionchange)

    layout.addWidget(self.cb)
    self.setLayout(layout)
    self.cb.textbox = QLineEdit(self)
    self.cb.textbox.move(100, 200)
    self.cb.textbox.resize(150,50)
    self.cb.textbox.setAlignment(Qt.AlignCenter)
    self.cb.textbox.setText("Initial Text")

  def selectionchange(self):
    #self.cb.textbox.setText("Calculating...") # THIS IS NOT WORKING
    self.cb.currentIndexChanged.connect(self.selectionchange)
    # MATH CALCULATION CODE GOES HERE[...]
    self.cb.textbox.setText("RESULT OF MATH CALCULATION")

def main():
  app = QApplication(sys.argv)
  ex = combodemo()
  ex.show()
  sys.exit(app.exec_())

if __name__ == '__main__':
  main()

Right now the program freezes for 3 seconds after a Key is selected (because of the math calculation I am doing). Since my math calculation takes 3 seconds, I would like to update the textbox to "Calculating..." once a Key from the QComboBox is selected. So while my math calculation is performed I want the textbox to show "Calculating...". Once the calculation is done it should simple rewrite the textbox and show the math result.

In the above code I tried to achieve it in the function def selectionchange(self) right at the beginning (it's uncommented). But it has no effect. How can I make this work?

Chris
  • 157
  • 2
  • 11

2 Answers2

0

This is how most GUIs works: GUI runs loop (called mainloop or event loop) in which it does many thinks in some order: it gets mouse/keyboard events, sends them to widgets, runs your functions, does other stuff and at the end updates/redraws widgets. So when you change text in your function it doesn't update/redraw it on the screen immediatelly. It does it later after it ends your function. So if you run long-running function then you don't see changed text immediatelly (and window doesn't response on mouse and keyboard).

You can use

app.processEvents()

to force updates but still you will have problem with response on mouse and keyboard events. You have to run calculation in second thread.

This code works for me but it is not ideal.

(second thread shouldn't change GUI directly - it should send message/signal to first thread. So there are better solution: Simplest way for PyQT Threading )

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import os,sys

import time # to simulate long-running function
import threading

dict1 = {"Key":100, "Key2":200, "Key3":300} # Value are int's

class ComboDemo(QWidget):

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

    layout = QHBoxLayout()
    self.cb = QComboBox()
    self.cb.addItems([key for key in sorted(dict1.keys())])
    self.cb.currentIndexChanged.connect(self.selectionchange)

    layout.addWidget(self.cb)
    self.setLayout(layout)
    self.cb.textbox = QLineEdit(self)
    self.cb.textbox.move(100, 200)
    self.cb.textbox.resize(150,50)
    self.cb.textbox.setAlignment(Qt.AlignCenter)
    self.cb.textbox.setText("Initial Text")

  def selectionchange(self):
    self.cb.textbox.setText("Calculating...")

    # get value from dict using selected key
    value = dict1[str(self.cb.currentText())]

    # run thread - args has to be tuple
    t = threading.Thread(target=self.calculation, args=(value, ) ) 
    t.start()

  def calculation(self, arg):
    # MATH CALCULATION CODE GOES HERE[...]
    time.sleep(3) # to simulate long-running function
    self.cb.textbox.setText("RESULT: " + str(arg))


def main():

  app = QApplication(sys.argv)
  ex = ComboDemo()
  ex.show()
  sys.exit(app.exec_())

if __name__ == '__main__':
  main()
Community
  • 1
  • 1
furas
  • 134,197
  • 12
  • 106
  • 148
0

You could you a progress bar to show the user that calculations are underway. Here is an example (the entire code is at http://zetcode.com/gui/pyqt4/widgets/ :

def initUI(self):      

    self.pbar = QtGui.QProgressBar(self)
    self.pbar.setGeometry(30, 40, 200, 25)

    self.btn = QtGui.QPushButton('Start', self)
    self.btn.move(40, 80)
    self.btn.clicked.connect(self.doAction)

    self.timer = QtCore.QBasicTimer()
    self.step = 0


def timerEvent(self, e):

    if self.step >= 100:

        self.timer.stop()
        self.btn.setText('Finished')
        return

    self.step = self.step + 1
    self.pbar.setValue(self.step)

def doAction(self):

    if self.timer.isActive():
        self.timer.stop()
        self.btn.setText('Calculating ...')

    else:
        self.timer.start(100, self)
        self.btn.setText('Stop')
rainer
  • 3,295
  • 5
  • 34
  • 50