我需要创建一个client-server注册表系统。客户端应该输入其用户名和密码,服务器应确认。 GUI应在tkinter中制作。 问题是,在检查服务器的命令之后,我需要添加标签。 如果我对这是错误的理解,我非常抱歉,因为我习惯了pygame,其中一切都在1 wher(true)循环中起作用。
这是客户端代码。协议是一个不同的文件,其中包含某种命令检查。 服务器不是问题的一部分。确认客户端请求时,它将发送"registered"。
import socket
import sys
import tkinter as tk
from tkinter.simpledialog import askstring
from tkinter import *
from tkinter import messagebox
import time
from queue import Queue
import threading
import protocol
data_queue = Queue() # queue where threads can send data from one to another
IP = "127.0.0.1"
PORT = 1234
BIG_BUFFER = 256
stop_event = threading.Event()
def packed(cmd):
return cmd.encode()
def on_closing(top, client, data_thread):
if messagebox.askokcancel("Quit", "Do you want to close the application?"):
client.send(packed("exit"))
stop_event.set()
top.destroy()
data_thread.join()
client.close()
def receive_data_from_server(client): # receives data from server through another thread
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):
data_queue.put(server_cmd) # sends to main thread
else:
print("invalid cmd: " + server_cmd)
except Exception as err:
print(err)
def password_err():
print("h")
def register_user(client, username, password1, password2):
if password1 == password2:
client.send(packed("add-" + username + "-" + password1))
else:
password_err()
def success_reg(frame):
frame.insert(tk.END, "Success!")
frame.pack()
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
top = tk.Tk()
top.title("Registration")
top.geometry("800x400")
top.protocol("WM_DELETE_WINDOW", lambda: on_closing(top, client, data_thread))
registration_frame = tk.Frame(top)
registration_frame.pack()
register_label = tk.Label(registration_frame, text="Registration")
register_label.pack()
username_label = tk.Label(registration_frame, text="Enter username:")
username_label.pack()
username_entry = tk.Entry(registration_frame)
username_entry.pack()
password_label = tk.Label(registration_frame, text="Enter password:")
password_label.pack()
password_entry = tk.Entry(registration_frame, show="*") # "show" hides the password
password_entry.pack()
check_password_label = tk.Label(registration_frame, text="Confirm password:")
check_password_label.pack()
check_password_entry = tk.Entry(registration_frame, show="*") # "show" hides the password
check_password_entry.pack()
# creates register button
register_button = tk.Button(registration_frame, text="Register", command=lambda: register_user(
client, username_entry.get(), password_entry.get(), check_password_entry.get()))
register_button.pack()
# data thread listening to server
data_thread = threading.Thread(target=receive_data_from_server, args=(client,))
data_thread.daemon = True
data_thread.start()
top.mainloop()
while not stop_event.isSet():
if not data_queue.empty():
server_cmd = data_queue.get()
if server_cmd == "registered":
success_reg(registration_frame)
if __name__ == '__main__':
main()
提前致谢。
分析解答
在您的main()
功能中,您只需创建所有GUI元素,然后使用top.mainloop()
启动GUI应用程序。没有用于接收和处理服务器消息的循环。而且它不能存在,因为在调用top.mainloop()
之后,main()
函数在该线路完全冻结。
为了使GUI对来自服务器的消息反应,通常您应该单独进行线程,在此中将在无限循环中读取队列。
在main()
功能中,您已经创建了线程data_thread
。但是,在receive_data_from_server
内部,而不是收集到data_queue
的消息,您可以简单地为GUI编写逻辑以对消息做出反应。另外,您应该将GUI元素传递给receive_data_from_server
,您想对消息做出反应(以使其可用于功能)。
这是快速示例应该如何工作的(我还通过重写代码,保持逻辑相同的几个缩进级别):
def receive_data_from_server(client, window): # receives data from server through another thread
while not stop_event.isSet():
try:
server_cmd = client.recv(BIG_BUFFER).decode()
# If nothing recieved - go to next loop iteration
if not server_cmd:
continue
# If recieved invalid command - go to next loop iteration
if protocol.check_cmd(server_cmd):
print("invalid cmd: " + server_cmd)
continue
# At this point server_cmd is validated
# Now we can write logic to different server commands
# If revieved "REGISTER" from server
if server_cmd == "REGISTER":
# Create new label
new_label = tk.Label(window, text="You've successfully registered!")
# If revieved "ANYTHING_ELSE" from server
elif server_cmd == "ANYTHING_ELSE":
# Create new label
new_label = tk.Label(window, text="Something happened...")
# If revieved "SOMETHING_ELSE" from server
elif server_cmd == "SOMETHING_ELSE":
# Create new label
new_label = tk.Label(window, text="Something else happened...")
# After new label is created, just pack it to GUI
new_label.pack()
except Exception as err:
print(err)
# A little sleep is good to prevent infinite loop from spamming
# You should import it: from time import sleep
sleep(0.5)
之后,您应该更改data_thread
创建过程,并通过应达到的GUI元素。在这里,我仅通过了应用程序的top
窗口:
data_thread = threading.Thread(target=receive_data_from_server, args=(client, top))