0

I have noticed that there are a lot of users, myself included, who don't quite grasp the concept of signals and slots in Qt. I was hoping to get some clarification on the following:

#I have a function that runs as soon as the GUI is built, this takes the information from     
#a list and puts it into a string which is then uploaded to a texbox. At the bottom of this 
#loop, I want it to call a function in the parent thread via signals and slots, as 
#recommended by other users.
class MainWindow(QtGui.QMainWindow):
    #all the code needed to build the GUI
    thread_mythread = threading.Thread(target = self.updateText, args = ())
    thread_mythread.start()

    def clearText(self):
        self.TextEdit.clear()

    def updateText(self):
        self.trigger.connect(self.clearText)

        while True:
            self.trigger.emit()
            NewString = list.pop(0)
            #I think I may have to use append, as setText() is not safe outside of the parent thread
            self.TextEdit.append(NewString)

Although probably terribly incorrect, I attempt to use signals. Is this the proper way to do it? I also get an error that says that the Main Window object has no attribute "trigger",why is this?

thank you.

alexey
  • 706
  • 5
  • 9
sudobangbang
  • 1,406
  • 10
  • 32
  • 55

2 Answers2

2

The reason you get that error is exactly the reason described by the error message - the signal trigger has not been defined anywhere in your class. You need to define it before you can emit it.

Signals and slots are used to communicate between different objects. In your example you are trying to do everything from within your MainWindow class and there is no interaction with other objects. You also only need to make the call to connect() once. You would typically call this either in the class constructor or from your main function after instantiating the objects you want to connect together.

Take a look at http://pyqt.sourceforge.net/Docs/PyQt4/new_style_signals_slots.html for some examples of how to use signals and slots properly in PyQt.

For threading, use QThread rather than threading.Thread as it is better integrated with the Qt framework. This post shows some simple examples of how to use QThread in PyQt. The second method (using moveToThread()) is considered to be the most correct way to create new threads.

The basic idea for your kind of problem is:

  • handle GUI operations from the main thread
  • handle blocking operations (in your case the while loop) in a separate thread
  • emit signals from the worker thread to call functions (slots) in the main thread and vice versa

Also note that:

  • You cannot call any methods of QWidget its descendents from a secondary thread
  • Signals can also send data if you need to pass it between threads
Community
  • 1
  • 1
user3419537
  • 4,740
  • 2
  • 24
  • 42
  • This was very helpful. I cannot handle this kind of checking in the main thread because the infinite loop will down the GUI. The seperate thread unfortunately wont work either, this is because you cannot access certain Qt elements outside of the Parent thread (the textEdit.clear() method for example). Wasn't my example provided in the question an example of emitting signals from a worker thread(thread_myThread) to call functions (clearText()) in the main thread? @user3419537 – sudobangbang Jun 26 '14 at 17:25
  • Also, how would I go about defining a trigger? – sudobangbang Jun 26 '14 at 17:27
  • This is exactly why you want to use signals and slots. If you define clearText() to be a slot and connect it to a signal in your worker thread, the thread is signalling the main thread to call textEdit.clear(), not calling it directly. The first link I posted shows you how to define a signal (trigger). Just use pyqtSignal() – user3419537 Jun 26 '14 at 18:04
2

To add to @user3419537 good answer. A very quick threading example:

from PyQt4.QtCore import QObject, pyqtSlot, pyqtSignal, QThread, \
    Q_ARG, Qt, QMetaObject

class MyWorker(QObject):
    # define signal
    clear = pyqtSignal()
    update_text_signal = pyqtSignal(str)  # passes a string back
    finished = pyqtSignal()

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


    # Add functions etc.
    @pyqtSlot(list)
    def update_text(self, string_list):
        #Intensive operation
        self.clear.emit()  # moved outside of while
        while(True):
            #This is infinite loop so thread runs forever
            new_string = self.string_list.pop(0)
            self.update_text_signal.emit(new_string)  # Fixed this line

        #Finished
        self.finished.emit()

Then in your MainWindow class

self.my_thread = QThread()
self.handler = MyWorker()
self.handler.moveToThread(self.my_thread)
self.handler.clear.connect(self.clearText)
self.handler.update_text_signal.connect(self.update_line_edit)
self.handler.finished.connect(self.my_thread.quit)
# Start Thread
self.my_thread.start()

@pyqtSlot(str)
def update_line_edit(self, text):
    self.TextEdit.append(text)

QMetaObject.invokeMethod(self.handler, 'update_text',
                         Qt.QueuedConnection,
                         Q_ARG(list, string_list))

You will need to call self.my_thread.quit() before your application closes to stop the thread and avoid the error: QThread: Destroyed while thread is still running

Please read docs for QMetaObject.invokeMethod.

Shadow9043
  • 2,140
  • 1
  • 15
  • 13
  • This is very helpful, it says however that update_text has no attribute connect – sudobangbang Jun 27 '14 at 11:23
  • Sorry. Because I named the signal the same as a function it didn't know what to use. Please see amended code above. – Shadow9043 Jun 27 '14 at 11:27
  • Thats what I figured, but now I am getting the following message: `QThread: Destroyed while thread is still running` and I have no visible action occurring in my textbox – sudobangbang Jun 27 '14 at 11:38
  • I have even put a debugging statement in the update_text function and it is not reaching it. – sudobangbang Jun 27 '14 at 11:40
  • 1
    Changed code again. Please try that. I don't have access to PyQt environment at the moment so cant test the code properly. – Shadow9043 Jun 27 '14 at 12:24