[英]Update Tkinter UI from another thread
I am programming a socket application, I use build-in library socket
to make the server, Thread
and Tkinter
to make GUI.我正在编写一个套接字应用程序,我使用内置库socket
制作服务器,使用Thread
和Tkinter
制作 GUI。
My problem is I do not know how to insert text into listbox of class MainPage
(it is self.msg_list
) in main_page.py
from function __accept_incoming_connection
in class Server
in server.py
.我的问题是我不知道如何从self.msg_list
MainPage
__accept_incoming_connection
main_page.py
Server
server.py
Here's my project folder structure:这是我的项目文件夹结构:
server
├── main.py
├── server.py
├── views
│ ├── setup_page.py
│ ├── main_page.py
main.py
import tkinter as tk
from tkinter import font as tkfont
from view.setup_page import SetupPage
from view.main_page import MainPage
from server import Server
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica',
size=18,
weight='bold',
slant='italic')
container = tk.Frame(self)
container.pack(side='top', fill='both', expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (SetupPage, MainPage):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky='nsew')
self.show_frame('SetupPage')
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
if __name__ == '__main__':
server = Server()
app = App()
app.title('Server')
app.geometry('600x450')
app.mainloop()
server.__del__()
server.py
from socket import socket, AF_INET, SOCK_STREAM
from threading import Thread
HOST = ''
PORT = 3000
ADDRESS = (HOST, PORT)
class Server:
__instance = None
__is_running = False
@staticmethod
def get_instance():
if Server.__instance is None:
Server()
return Server.__instance
def __init__(self):
if Server.__instance is not None:
raise Exception("Server is singleton class")
else:
Server.__instance = self
self.__server = socket(AF_INET, SOCK_STREAM)
self.__server.bind(ADDRESS)
self.__is_running = True
def __del__(self):
self.__is_running = False
self.__accept_thread.join()
self.__server.close()
def __accept_incoming_connection(self):
while self.__is_running:
client, client_address = self.__server.accept()
# I want to insert text into listbox of the MainPage (msg_list) here
def listen(self, num_of_clients):
self.__server.listen(num_of_clients)
self.__accept_thread = Thread(target=self.__accept_incoming_connection)
self.__accept_thread.start()
main_page.py
import tkinter as tk
from socket import AF_INET, socket, SOCK_STREAM
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.scrollbar = tk.Scrollbar(self)
self.msg_list = tk.Listbox(self,
height=15,
width=50,
yscrollcommand=self.scrollbar.set)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.msg_list.pack(side=tk.LEFT, fill=tk.BOTH)
self.msg_list.pack()
You can use queue.SimpleQueue
to pass data from Server
to MainPage
so that MainPage
can insert the data into the Listbox
:您可以使用queue.SimpleQueue
将数据从Server
传递到MainPage
以便MainPage
可以将数据插入Listbox
:
main.py
: main.py
:
from queue import SimpleQueue
...
class App(tk.Tk):
def __init__(self, queue, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.queue = queue
...
if __name__ == "__main__":
queue = SimpleQueue()
server = Server(queue) # pass queue to Server
app = App(queue) # pass queue to App
...
server.py
: server.py
:
class Server:
...
def __init__(self, queue=None):
...
self.queue = queue
def __accept_incoming_connection(self):
while self.__is_running:
client, client_address = self.__server.accept()
# I want to insert text into listbox of the MainPage (msg_list) here
if self.queue:
self.queue.put(client_address) # or whatever information you want
...
main_page.py
: main_page.py
:
class MainPage(tk.Frame):
def __init__(self, parent, controller):
....
# call a monitor task periodically
self.monitor_queue()
def monitor_queue(self):
if not self.controller.queue.empty():
caddr = self.controller.queue.get()
self.msg_list.insert(tk.END, str(caddr))
self.msg_list.see(tk.END)
self.after(100, self.monitor_queue)
Below is an example using similar structure as your code:下面是一个使用与您的代码类似的结构的示例:
import tkinter as tk
from queue import SimpleQueue
from socket import socket, AF_INET, SOCK_STREAM
import threading
HOST = ""
PORT = 3000
ADDRESS = (HOST, PORT)
class Server(threading.Thread):
__instance = None
@staticmethod
def get_instance():
if Server.__instance is None:
Server()
return Server.__instance
def __init__(self, queue=None):
if Server.__instance:
raise Exception("Server is singleton class")
super().__init__()
Server.__instance = self
self._server = socket(AF_INET, SOCK_STREAM)
self._server.bind(ADDRESS)
self._server.listen()
self.queue = queue
def set_queue(self, queue):
self.queue = queue
def run(self):
print("Server started")
while True:
try:
client, client_address = self._server.accept()
if self.queue:
self.queue.put(client_address)
except Exception as e:
print(e)
break
print("Server terminated")
def terminate(self):
self._server.close() # self._server.accept() will raise exception and break the while loop
class SetupPage(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
tk.Label(self, text="Setup Page").pack(padx=100, pady=50)
tk.Button(self, text="Goto Main page", command=lambda: self.controller.show_frame("MainPage")).pack()
class MainPage(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
self.scrollbar = tk.Scrollbar(self)
self.msg_list = tk.Listbox(self, width=50, height=15, yscrollcommand=self.scrollbar.set)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.msg_list.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.monitor_queue()
def monitor_queue(self):
if not self.controller.queue.empty():
caddr = self.controller.queue.get()
self.msg_list.insert(tk.END, str(caddr))
self.msg_list.see(tk.END)
self.after(100, self.monitor_queue)
class App(tk.Tk):
def __init__(self, queue):
super().__init__()
self.queue = queue
container = tk.Frame(self)
container.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (SetupPage, MainPage):
frame = F(parent=container, controller=self)
frame.grid(row=0, column=0, sticky="nsew")
self.frames[F.__name__] = frame
self.show_frame("SetupPage")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
if __name__ == "__main__":
queue = SimpleQueue()
server = Server(queue)
server.start()
app = App(queue)
app.title("Server")
app.geometry("600x450")
app.mainloop()
server.terminate()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.