代码之家  ›  专栏  ›  技术社区  ›  oz hagevermeleh

如何将两个变量相互传递?

  •  -1
  • oz hagevermeleh  · 技术社区  · 1 年前

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

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

    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()
    
    
    1 回复  |  直到 1 年前
        1
  •  -1
  •   Uzair Jan    1 年前

    在创建应用程序变量后将其传递给线程,而不是在线程创建过程中尝试传递,因为操作顺序的原因,这是不起作用的。

    使用锁确保主线程和数据线程都能安全地访问和修改应用程序变量。 这是修改后的代码:

    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()
    

    通过这种方式,在应用程序对象完全初始化后,将应用程序变量传递给receive_data_from_server线程。此外,锁用于确保从主线程和数据线程安全访问应用程序变量。