You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将客户端名称添加到Listbox?解决AttributeError问题

多进程下Tkinter Listbox无法显示客户端名称问题解决

问题场景

我有一个Tkinter Listbox,期望服务器接受客户端连接时自动显示客户端名称,当前服务器和客户端代码如下:

服务器代码

class GUI2(GUI): #server GUI

    def __init__(self):

        self.clientlist = tk.Listbox(self.clientframe) #listbox  that should display client names
        self.clientlist.pack(expand = 1)

        self.s = INITSERVER()
    
        self.process = Process(target = self.s.startChat) #prevents the GUI from freezing due to server running

        self.process.start()

class INITSERVER(GUI2): 

      def startChat(self): #starts the server
   
          print("server is working on " + self.SERVER)
    
          self.server.listen(30) #sets max number to only 30 clients

          while True:
            
                self.conn, self.addr = self.server.accept()
            
                self.name = self.conn.recv(1024).decode(self.FORMAT)

                self.clientlist.insert("end", self.name) #append client names to listbox supposedly

                print(f"Name is :{self.name}")

客户端代码

class INITCLIENT(GUI3): #GUI3 is client GUI; calls INITCLIENT when done packing

    def __init__(self):

        self.PORT = 5000
        self.SERVER = "" #left blank for this post; contains the server's exact address
        self.ADDRESS = (self.SERVER, self.PORT)
        self.FORMAT = "utf-8"

        self.client = socket.socket(socket.AF_INET,
                   socket.SOCK_STREAM)

        self.client.connect(self.ADDRESS)

        self.name = g.entergname.get() # g = GUI() i.e. root window; entergname is Entry widget where client inputs their names

        self.client.send(self.name.encode(self.FORMAT)) #sends inputted names to INITSERVER to display in listbox.... supposedly

报错信息

运行服务器后,客户端连接时出现以下错误:

Process Process-1:
Traceback (most recent call last):
  File "F:\Program Files (x86)\Python\lib\multiprocessing\process.py", line 314, in _bootstrap
    self.run()
  File "F:\Program Files (x86)\Python\lib\multiprocessing\process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "f:\project\mainmenu.py", line 341, in startChat
    self.clientlist.insert("end", self.name) #append client names to listbox
AttributeError: 'INITSERVER' object has no attribute 'clientlist'

解决方案

根本原因

  1. 多进程内存空间完全独立,子进程无法访问主进程创建的Tkinter组件(clientlist),这是报错的直接原因。
  2. Tkinter是单线程/单进程设计,禁止在非主进程/主线程中操作任何Tkinter组件,即使能访问到也会导致程序崩溃。

代码重构方案

通过multiprocessing.Queue实现进程间安全通信,将服务器子进程获取的客户端名称传递给主GUI进程,由主进程更新Listbox:

修改后的服务器代码

import tkinter as tk
import multiprocessing
import socket

class GUI2(GUI): # server GUI
    def __init__(self):
        super().__init__() # 调用父类GUI的初始化方法
        self.clientframe = tk.Frame(self) # 假设父类已定义主窗口,此处创建clientframe容器
        self.clientframe.pack()

        self.clientlist = tk.Listbox(self.clientframe)
        self.clientlist.pack(expand=1)

        # 创建进程间通信队列,用于传递客户端名称
        self.client_queue = multiprocessing.Queue()

        # 初始化服务器,传入队列
        self.s = INITSERVER(self.client_queue)
        self.process = multiprocessing.Process(target=self.s.startChat)
        self.process.start()

        # 启动定期检查队列的任务,更新Listbox
        self.check_client_queue()

    def check_client_queue(self):
        # 读取队列中所有新增的客户端名称
        while not self.client_queue.empty():
            client_name = self.client_queue.get()
            self.clientlist.insert("end", client_name)
        # 每隔100毫秒重新检查一次
        self.after(100, self.check_client_queue)

class INITSERVER:
    def __init__(self, client_queue):
        self.SERVER = "0.0.0.0" # 替换为实际服务器地址
        self.PORT = 5000
        self.FORMAT = "utf-8"
        self.client_queue = client_queue

        # 初始化socket服务器
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.bind((self.SERVER, self.PORT))

    def startChat(self):
        print(f"server is working on {self.SERVER}:{self.PORT}")
        self.server.listen(30)
        while True:
            conn, addr = self.server.accept()
            name = conn.recv(1024).decode(self.FORMAT)
            print(f"Name is :{name}")
            # 将客户端名称放入队列,由主GUI进程处理更新
            self.client_queue.put(name)
            # 此处可添加后续通信逻辑,比如保存conn对象用于消息转发

客户端代码(仅补充服务器地址)

import socket

class INITCLIENT(GUI3): #GUI3 is client GUI; calls INITCLIENT when done packing
    def __init__(self):
        super().__init__()
        self.PORT = 5000
        self.SERVER = "服务器实际IP地址" # 填写服务器的真实地址
        self.ADDRESS = (self.SERVER, self.PORT)
        self.FORMAT = "utf-8"

        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.client.connect(self.ADDRESS)

        self.name = g.entergname.get() # g = GUI() i.e. root window; entergname is Entry widget where client inputs their names
        self.client.send(self.name.encode(self.FORMAT))

关键改动说明

  • 解耦服务器与GUI:INITSERVER不再继承GUI2,作为独立的服务器类通过队列传递数据,代码结构更清晰易维护。
  • 进程间安全通信:使用multiprocessing.Queue在子进程和主进程之间传递客户端名称,避免跨进程直接操作GUI组件。
  • GUI安全更新:主GUI进程通过after方法定期轮询队列,确保所有Tkinter组件操作都在主进程中执行,符合Tkinter设计规范。

内容的提问来源于stack exchange,提问作者N. Tanaka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 19:56:08