如何互相传递2个变量?

问题描述 投票:0回答:1

我正在使用 tkinter 作为 GUI 制作一个客户端-服务器系统,我需要通过与服务器通信的线程传递 app 变量。问题是,我还需要通过应用程序传递线程。

以下是我到目前为止所尝试过的,但我根本不知道在这种情况下该怎么做。 如果我的做法明显不正确,请纠正我。我不是专业人士,所以我可能完全以错误的方式看待它。

import tkinter as tk
import socket
import sys
from tkinter.simpledialog import askstring
from tkinter import *
from tkinter import messagebox
import time
import threading
import protocol


IP = "127.0.0.1"
PORT = 1234
BIG_BUFFER = 256
stop_event = threading.Event()

logged_in = False


def packed(cmd):
    return cmd.encode()


def receive_data_from_server(client, app):
    while not stop_event.isSet():
        try:
            server_cmd = client.recv(BIG_BUFFER).decode()
            if server_cmd:
                print(server_cmd)
                if protocol.check_cmd(server_cmd):
                    app.handle_server_response(server_cmd)
                else:
                    print("invalid cmd: " + server_cmd)
        except Exception as err:
            print(err)


def handle_server_response(self, response):
    if response == "log_in_acc":
        self.controller.login_successful()
    elif response == "log_in_err":
        self.error_label.config(text="Invalid username or password")
    else:
        print("Unknown response from server:", response)



class LogInWindow(tk.Tk):
    def __init__(self, client, data_thread, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.client = client
        self.data_thread = data_thread

        self.title("Log In")
        self.geometry("500x400")
        self.protocol("WM_DELETE_WINDOW", self.on_closing)

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(1, weight=1)

        self.frames = {}
        for F in (LoginPage, RegisterPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=1, sticky="nsew")

        # Initially show LoginPage
        self.show_frame(LoginPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

    def login_successful(self):
        # Destroy the current main application
        self.destroy()
        # Create and start a new application
        global logged_in
        logged_in = True

    def on_closing(self):
        if messagebox.askokcancel("Quit", "Do you want to close the application?"):
            self.client.send(packed("exit"))
            stop_event.set()
            self.destroy()
            self.data_thread.join()

    def send(self, msg):
        self.client.send(packed(msg))


class LoginPage(tk.Frame):
    def __init__(self, parent, controller):
        super().__init__(parent)

        self.controller = controller

        label = tk.Label(self, text="Login Page", font=("Helvetica", 18))
        label.pack(pady=10, padx=10)

        username_label = tk.Label(self, text="Username:")
        username_label.pack()
        self.username_entry = tk.Entry(self)
        self.username_entry.pack()

        password_label = tk.Label(self, text="Password:")
        password_label.pack()
        self.password_entry = tk.Entry(self, show="*")
        self.password_entry.pack()

        login_button = tk.Button(self, text="Login", command=self.login)
        login_button.pack(pady=5)

        register_button = tk.Button(self, text="Register", command=lambda: controller.show_frame(RegisterPage))
        register_button.pack(pady=5)

        self.error_label = tk.Label(self, text="", fg="red")
        self.error_label.pack(pady=5)

    def login(self):
        username = self.username_entry.get()
        password = self.password_entry.get()

        self.controller.send("log_in-" + username + "-" + password)
        if username == "user" and password == "password":  # todo send log in request and receive
            self.controller.login_successful()
        else:
            self.error_label.config(text="Invalid username or password")


def main():
    try:
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect((IP, PORT))  # connect to server

    except Exception as error:
        print(error)  # could be an error connecting to server or establishing the socket

    data_thread = threading.Thread(target=receive_data_from_server,
                                   args=(client, app))
    data_thread.daemon = True
    data_thread.start()

    app = LogInWindow(client, data_thread)
    data_thread.app = app
    if not stop_event.isSet():
        app.mainloop()


    time.sleep(0.1)
    client.close()


if __name__ == "__main__":
    main()

python multithreading tkinter client
1个回答
0
投票

在创建线程后将 app 变量传递给线程,而不是在线程创建期间尝试传递它,由于操作顺序,这将不起作用。

使用锁来保证app变量能够被主线程和数据线程安全地访问和修改。 这是修改后的代码:

import tkinter as tk
import socket
import sys
from tkinter.simpledialog import askstring
from tkinter import *
from tkinter import messagebox
import time
import threading
import protocol

IP = "127.0.0.1"
PORT = 1234
BIG_BUFFER = 256
stop_event = threading.Event()

logged_in = False

def packed(cmd):
    return cmd.encode()

def receive_data_from_server(client, app):
    while not stop_event.isSet():
        try:
            server_cmd = client.recv(BIG_BUFFER).decode()
            if server_cmd:
                print(server_cmd)
                if protocol.check_cmd(server_cmd):
                    app.handle_server_response(server_cmd)
                else:
                    print("invalid cmd: " + server_cmd)
        except Exception as err:
            print(err)

class LogInWindow(tk.Tk):
    def __init__(self, client, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.lock = threading.Lock()  # Lock for accessing app variable
        self.client = client

        self.title("Log In")
        self.geometry("500x400")
        self.protocol("WM_DELETE_WINDOW", self.on_closing)

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(1, weight=1)

        self.frames = {}
        for F in (LoginPage, RegisterPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=1, sticky="nsew")

        # Initially show LoginPage
        self.show_frame(LoginPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

    def login_successful(self):
        # Destroy the current main application
        self.destroy()
        # Create and start a new application
        global logged_in
        logged_in = True

    def on_closing(self):
        if messagebox.askokcancel("Quit", "Do you want to close the application?"):
            self.client.send(packed("exit"))
            stop_event.set()
            self.destroy()

    def send(self, msg):
        self.client.send(packed(msg))


class LoginPage(tk.Frame):
    def __init__(self, parent, controller):
        super().__init__(parent)

        self.controller = controller

        label = tk.Label(self, text="Login Page", font=("Helvetica", 18))
        label.pack(pady=10, padx=10)

        username_label = tk.Label(self, text="Username:")
        username_label.pack()
        self.username_entry = tk.Entry(self)
        self.username_entry.pack()

        password_label = tk.Label(self, text="Password:")
        password_label.pack()
        self.password_entry = tk.Entry(self, show="*")
        self.password_entry.pack()

        login_button = tk.Button(self, text="Login", command=self.login)
        login_button.pack(pady=5)

        register_button = tk.Button(self, text="Register", command=lambda: controller.show_frame(RegisterPage))
        register_button.pack(pady=5)

        self.error_label = tk.Label(self, text="", fg="red")
        self.error_label.pack(pady=5)

    def login(self):
        username = self.username_entry.get()
        password = self.password_entry.get()

        self.controller.send("log_in-" + username + "-" + password)
        if username == "user" and password == "password":  # todo send log in request and receive
            self.controller.login_successful()
        else:
            self.error_label.config(text="Invalid username or password")


def main():
    try:
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect((IP, PORT))  # connect to server

    except Exception as error:
        print(error)  # could be an error connecting to server or establishing the socket

    app = LogInWindow(client)
    data_thread = threading.Thread(target=receive_data_from_server, args=(client, app))
    data_thread.daemon = True
    data_thread.start()

    app.mainloop()

    stop_event.set()  # Signal the data thread to stop
    data_thread.join()  # Wait for the data thread to finish
    client.close()

if __name__ == "__main__":
    main()

这样,app 对象完全初始化后,app 变量就会传递到 receive_data_from_server 线程。此外,还使用锁来确保从主线程和数据线程安全访问 app 变量。

© www.soinside.com 2019 - 2024. All rights reserved.