Question

J'ai décidé d'ajouter une interface à l'un de mes scripts.Le script est un simple web grattoir.J'ai décidé d'utiliser un thread de travail, le téléchargement et l'analyse des données peut prendre un certain temps.J'ai décidé d'utiliser PySide, mais ma connaissance de l'intervalle Qt en général est assez limité.

Comme le script est censé attendre une entrée de l'utilisateur au moment de l'entrée à travers un captcha, j'ai décidé qu'il devrait attendre jusqu'à ce qu'un QLineEdit les feux de returnPressed et puis de l'envoyer du contenu à l'thread de travail de sorte qu'il peut l'envoyer pour validation.Qui devrait être mieux que occupé-en attendant le retour touche à presser.

Il semble que l'attente d'un signal n'est pas aussi simple que je le pensais et après avoir cherché pendant un moment je suis tombé sur plusieurs solutions similaires à cette.La signalisation dans les threads et un événement local en boucle dans le thread de faire ma solution un peu plus compliqué cependant.

Après bricoler avec elle pendant plusieurs heures, cela ne fonctionne toujours pas.

Ce qui est censé se produire:

  • Téléchargement de données jusqu'à ce que appelé captcha et entrer dans une boucle
  • Télécharger le captcha et l'afficher à l'utilisateur, démarrage QEventLoop en appelant self.loop.exec_()
  • Sortie QEventLoop en appelant loop.quit() dans un worker threads fente qui est connecté via self.line_edit.returnPressed.connect(self.worker.stop_waiting) dans le main_window classe
  • Valider le captcha et boucle si la validation échoue, sinon réessayer la dernière url, ce qui devrait être téléchargeable maintenant, pour passer ensuite à l'url suivante

Ce qui se passe:

  • ...voir ci-dessus...

  • La sortie QEventLoop ne fonctionne pas. self.loop.isRunning() retourne False après l'appel de son exit(). self.isRunning retourne True, ce fil ne semble pas mourir sous d'étranges circonstances.Toujours le thread s'arrête à la self.loop.exec_() ligne.En tant que tel, le thread est bloqué l'exécution de la boucle d'événement, même si la boucle d'événements me dit qu'elle ne fonctionne pas plus.

  • Le GUI répond que les fentes du travailleur de la classe thread.Je peux voir le texte étant de l'envoyer à la thread de travail, le statut de la boucle d'événement et le fil lui-même, mais rien, après l'alinéa ci-dessus est exécuté.

Le code est un peu compliqué, j'ai ajouter un peu de pseudo-code python mélange en laissant de côté le peu d'importance:

class MainWindow(...):
    # couldn't find a way to send the text with the returnPressed signal, so I
    # added a helper signal, seems to work though. Doesn't work in the
    # constructor, might be a PySide bug?
    helper_signal = PySide.QtCore.Signal(str)
    def __init__(self):
        # ...setup...
        self.worker = WorkerThread()
        self.line_edit.returnPressed.connect(self.helper_slot)
        self.helper_signal.connect(self.worker.stop_waiting)

    @PySide.QtCore.Slot()
    def helper_slot(self):
        self.helper_signal.emit(self.line_edit.text())

class WorkerThread(PySide.QtCore.QThread):
    wait_for_input = PySide.QtCore.QEventLoop()

    def run(self):
        # ...download stuff...
        for url in list_of_stuff:
            self.results.append(get(url))

    @PySide.QtCore.Slot(str)
    def stop_waiting(self, text):
        self.solution = text
        # this definitely gets executed upon pressing return
        self.wait_for_input.exit()

    # a wrapper for requests.get to handle captcha
    def get(self, *args, **kwargs):
        result = requests.get(*args, **kwargs)
        while result.history: # redirect means captcha
            # ...parse and extract captcha...
            # ...display captcha to user via not shown signals to main thread...

            # wait until stop_waiting stops this event loop and as such the user
            # has entered something as a solution
            self.wait_for_input.exec_()

            # ...this part never get's executed, unless I remove the event
            # loop...

            post = { # ...whatever data necessary plus solution... }
            # send the solution
            result = requests.post('http://foo.foo/captcha_url'), data=post)
        # no captcha was there, return result
        return result

frame = MainWindow()
frame.show()
frame.worker.start()
app.exec_()
Était-ce utile?

La solution

Le logement est exécutée dans le thread qui a créé le QThread, et pas dans le thread qui l' QThread les contrôles.

Vous avez besoin de déplacer un QObject au fil et de vous connecter sa fente pour le signal, et que le logement sera exécutée dans le thread:

class SignalReceiver(QtCore.QObject):
    def __init__(self):
        self.eventLoop = QEventLoop(self)             

    @PySide.QtCore.Slot(str)
    def stop_waiting(self, text):                   
        self.text = text
        eventLoop.exit()

    def wait_for_input(self):
        eventLoop.exec()
        return self.text

class MainWindow(...):
     ...
     def __init__(self):
        ...
        self.helper_signal.connect(self.worker.signalReceiver.stop_waiting)

class WorkerThread(PySide.QtCore.QThread): 
    def __init__(self):
        self.signalReceiver = SignalReceiver() 
        # After the following call the slots will be executed in the thread             
        self.signalReceiver.moveToThread(self)    

    def get(self,  *args, **kwargs):
        result = requests.get(*args, **kwargs)
        while result.history:
            ...
            self.result = self.signalReceiver.wait_for_input()   

Autres conseils

Ce que vous décrivez est idéal pour QWaitCondition.

Exemple Simple:

import sys
from PySide import QtCore, QtGui

waitCondition = QtCore.QWaitCondition()
mutex = QtCore.QMutex()

class Main(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Main, self).__init__()

        self.text = QtGui.QLineEdit()
        self.text.returnPressed.connect(self.wakeup)

        self.worker = Worker(self)
        self.worker.start()

        self.setCentralWidget(self.text)

    def wakeup(self):
        waitCondition.wakeAll()

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

    def run(self):
        print "initial stuff"

        mutex.lock()
        waitCondition.wait(mutex)
        mutex.unlock()

        print "after returnPressed"

if __name__=="__main__":      
    app = QtGui.QApplication(sys.argv)
    m = Main()
    m.show()
    sys.exit(app.exec_())
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top