0

私はPythonを初めて使用し、現在個人用の小さなアプリケーションを開発しています。GUIにtkinterを使用しています。

私がやろうとしているのは、ログイン試行の進行状況に応じてテキストを変更するラベル付きのトップレベル ポップアップを作成することです。したがって、tk が実行されているメイン スレッドが動的テキストを含むポップアップを表示している間に、スレッドを開始して最大 5 回ログインを試み、「logindata」というグローバル変数を設定してメイン スレッドに報告したいと考えています。

AuctioneerGUI の _login() メソッドと LoginThread クラスは、ここで重要な唯一のものであり、残りは無視できますが、関連があると見なされる場合があります。

ログインボタンが押されると、_login() メソッドが呼び出されます。これが行うことは、ログインを試みて logindata を設定することだけです。その間、メイン スレッドは、LoginThread が変数を設定したことに気付くまでループし、3 つすべてを収集すると、残りのロジックに進みます (完全には実装されていませんが、問題には関係ありません)。

ここで起こることは、LoginThread が開始された後にメイン スレッドが停止し、それが終了したときにのみ続行することです。LoginThread は別のスレッドで実行する必要があるため、メイン スレッドを停止しないでください。そのため、ポップアップは、LoginThread が実行するタスクが完了した後にのみ表示されます。ポップアップが表示され、ユーザーに更新を与えるラベルを表示したいと思います。どうすればいいですか?

印刷を使用してこれを判断したため、スレッドがメインスレッドを停止していることに問題があると確信しています。

また、もう 1 つ小さな質問があります。popup.destroy() は何もしていないようです。TopLevel はそこにとどまります。

テキストの壁で申し訳ありませんが、私を助けてくれてありがとう. 私はすでにいくつかの異なることを試す必要があるよりも多くの時間を費やしましたが、それを機能させることができませんでした.

何か不明な点があれば教えてください。時には非効率的またはばかげたロジックを気にしないでください。きれいにする前に、少なくとも機能するようにしたいと思います。

-大安

global logindata
logindata = {"counter": -1, "status": -1, "success": -1}

class AuctioneerGUI:
    def __init__(self):
        root = Tk()
        root.title("Path of Exile Auctioneer")
        self._setupGUI(root)
        self._loggingin = False

        root.protocol("WM_DELETE_WINDOW", lambda: root.quit())
        root.mainloop()           

    def _setupGUI(self, root):            
        frame = Frame(root)

        email = StringVar()
        pass_ = StringVar()
        thread = StringVar()

        email.set("email")
        pass_.set("password")
        thread.set("76300")

        email_label = Label(frame, text="email")
        self._email_box = Entry(frame, takefocus=True, width=50, textvariable=email)
        self._email_box.focus_set()
        pass_label = Label(frame, text="password")
        self._pass_box = Entry(frame, takefocus=True, show="*", width=50, textvariable=pass_)
        thread_label = Label(frame, text="thread id")
        self._thread_box = Entry(frame, takefocus=True, width=10, textvariable=thread)
        self._login_button = Button(frame, text="login", command=lambda: self._login(root), takefocus=True)

        frame.pack()
        email_label.pack()
        self._email_box.pack()
        pass_label.pack()
        self._pass_box.pack()
        thread_label.pack()
        self._thread_box.pack()
        self._login_button.pack()

    def _login(self, root):
        self._login_button.configure(command=None)
        email = self._email_box.get()
        pass_ = self._pass_box.get()
        thread = self._thread_box.get()
        # Check email validity
        # no whitespaces, 1 @ sign 1 . after the @ sign
        try:
            thread = int(thread)
        except ValueError:
            return -1
            #invalid thread

        if not re.match(r"[^@]+@[^@]+\.[^@]+", email) or not email.find(" ") == -1:
            return -1
            #invalid mail

        self._sm = SessionManager(email, pass_, thread)    

        self._message = StringVar()
        self._message.set("Attempt 1/5.")

        popup = Toplevel(root)
        popup.title("Logging in...")
        message_label = Label(popup, text = self._message.get(), textvariable = self._message)
        message_label.pack()

        _thread = LoginThread(self._sm)        
        _thread.start()

        loop = True                

        while loop:
            counter = -1
            success = -1
            status = -1
            while counter == -1:
                counter = logindata["counter"]
                print(counter)
            while success == -1:
                success = logindata["success"]
            print(success)
            while status == -1:
                status = logindata["status"]
            print(status)
            if success:
                self._message.set("Attempt {}/5. Success.".format(counter))
            elif status == 200:
                self._message.set("Attempt {}/5. Failed: wrong password.".format(counter))
            else:
                self._message.set("Attempt {}/5. Failed: connection error. {}".format(counter, status))
            updatebar = not success
            logindata["counter"] = -1
            logindata["status"] = -1
            logindata["success"] = -1
            if counter == 5:
                break

        popup.destroy()
        self._login_button["command"] = lambda: self._login(root)
        self._setup_main_layout(root)

    def _setup_main_layout(self, root):
        pass

class LoginThread(threading.Thread):

    def __init__(self, sessionmanager):
        threading.Thread.__init__(self)
        self._sm = sessionmanager

    def run(self):
        success = False
        counter = 1
        while not success:
            if counter > 5:
                break

            data = self._sm.login()
            status = data[1]
            success = data[0]
            logindata["counter"] = counter
            logindata["success"] = success
            logindata["status"] = status
            counter += 1
            print("done")

アップデート:

いくつかの調査の後、ウィジェットにパイプされ、次の例のようにキューを介して通信するラベルから継承する ThreadSafeLabel を作成することで、問題を解決します。

http://effbot.org/zone/tkinter-threads.htm

4

2 に答える 2

2

a を開始する適切な方法は、メソッドではなくメソッドthreading.Threadを呼び出すことです。新しいスレッドを生成するメソッドです。それがなければ、実際にはメインスレッドで実行されています。startrunstartLoginThread.run

代わりに試してください:

    _thread = LoginThread(self._sm)        
    _thread.start()

ドキュメントから:

スレッド オブジェクトが作成されたら、スレッドの start() メソッドを呼び出してそのアクティビティを開始する必要があります。これにより、別の制御スレッドで run() メソッドが呼び出されます。

于 2013-02-19T00:41:09.903 に答える
2

unutbu が指摘するようrunに、メイン スレッドで他のスレッドの関数を実行しているだけなので、それが完了するまで何も起こりません。


それを解決したら、ここで行うように、変数が変更されるのを待っている間にスレッドをスピンさせたくありません。

while counter == -1:
    counter = logindata["counter"]
    print(counter)

メインスレッドは、バックグラウンドスレッドが別のものに設定されるまで、ここでスピンする以外に何もできませlogindata["counter"]ん。他のスレッドが完了するまでメイン スレッドを強制的に待機させる場合は、メイン スレッドで他のコードを実行することもできます。あなたのコードは、理由もなく何度も何度も値をチェックすることを除いて、シングルスレッドで物事を行うのと同じ効果があります。

何かが完了するまで待機する必要がある場合は、ある種のクロススレッド シグナル ( athreading.Conditionまたは a など) を使用する必要がありますqueue.Queue


ただし、ログインが完了するまでメインスレッドが関数内でスタックするため、それでも問題は解決しません。_loginこれは、画面の再描画、マウス クリックの処理など、他のことを実行できないことを意味します。

したがって、最初の 2 つの問題を解決して問題が解決したとしても、これはスレッドを生成せず、メイン スレッドでログインするだけとまったく同じです。

必要なのは_login、バックグラウンド スレッドを開始した直後に戻り、他のメカニズムを使用してバックグラウンド スレッドから tkinter ループでイベントをトリガーする関数です。

于 2013-02-19T00:45:06.030 に答える