简体   繁体   English

从另一个线程更新 Tkinter UI

[英]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制作服务器,使用ThreadTkinter制作 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.

相关问题 tkinter:如何使用队列从另一个线程更新 Treeview 项目? - tkinter: How to update Treeview item from another thread using queue? 如何根据在另一个线程中运行的代码的结果更新 tkinter gui? - How to get tkinter gui to update based off of results from code running in another thread? 如何从另一个线程中的 Flask 应用程序更新 tkinter window 中的进度条值? - How to update the progress bar value in a tkinter window from a Flask application which is in another thread? 使用第二个线程中的数据更新Tkinter-GUI中的数据 - Update data in a Tkinter-GUI with data from a second Thread 从运行命令的单独线程更新 Tkinter GUI - Update Tkinter GUI from a separate thread running a command Python:从线程事件更新tkinter canvas小部件 - Python: update tkinter canvas widget from thread events 从 Tkinter (Python) 中的另一个线程更改 tk.canvas 的内容 - Change content of tk.canvas from another thread in the Tkinter (Python) 尝试从另一个线程更改tkinter标签时出错 - Error while trying to change tkinter label from another thread 为什么我必须在另一个 class 中创建我的线程,以避免冻结我的 Tkinter UI? - Why do i have to create my thread in another class, to avoid freezing my Tkinter UI? 如何自动更新 tkinter ui - How to automatically update a tkinter ui
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM