【问题标题】:Update Tkinter UI from another thread从另一个线程更新 Tkinter UI
【发布时间】:2021-03-31 02:14:53
【问题描述】:

我正在编写一个套接字应用程序,我使用内置库 socket 来制作服务器,ThreadTkinter 来制作 GUI。

我的问题是我不知道如何从server.py 中的__accept_incoming_connection 类中的__accept_incoming_connection 类中的main_page.py 中的MainPage 类的列表框中插入文本(它是self.msg_list)。

这是我的项目文件夹结构:

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

【问题讨论】:

标签: python multithreading tkinter


【解决方案1】:

您可以使用queue.SimpleQueue 将数据从Server 传递到MainPage,以便MainPage 可以将数据插入Listbox

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:

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:

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)

以下是使用与您的代码类似的结构的示例:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-30
    • 2014-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-15
    相关资源
    最近更新 更多