【问题标题】:Tkinter - Multithreading / Multiprocessing - Which one fits better in this code?Tkinter - 多线程/多处理 - 哪一个更适合这段代码?
【发布时间】:2020-04-06 18:41:00
【问题描述】:

我是这里的新手,所以如果我的观点不够清楚,请原谅。

我正在尝试改进几个月前使用 Tkinter 在套接字中编写的服务器。我使用了在互联网上找到的模板。在我到达 server.listening() 部分之前,一切似乎都很顺利。那时 Tkinter 将永远冻结。我知道这与多线程、多处理以及将该线程放在主循环之外的事实有关,但到目前为止我似乎找不到任何解决方案。

服务器实际上可以工作,即使每次我点击“监听”按钮时 Tkinter 都会冻结。所以,这是我的观点。它是否完全冻结 Tkinter 几秒钟并不重要,因为在至少有一个人连接到它之前,该服务器将无法做太多事情。我只希望程序能够在按下正确的按钮时激活“监听”,并在需要时将其停用。

你能解释一下我的疑问吗?我已经像 2 天一样不断地与这个作斗争,只设法走到这一步。我将把完整的代码留在这篇文章的最后,以防你认为有必要先运行它。所以这里是代码,为了简单起见删除了一些部分:

from socket import AF_INET, socket, SOCK_STREAM
from threading import Thread
from datetime import date
from tkinter import messagebox
from tkinter import *
from tkinter import ttk, font
from multiprocessing import Process


import getpass
import os
import time

global msgs
global opt
global ADDR
global MK1



def broadcast(msg, prefix=""):  # prefix is for name identification.
    """Broadcasts a message to all the clients."""

    for sock in clients:

        sock.send(bytes(prefix, "utf8")+msg)


class threads():



    def accept_incoming_connections():
        """Sets up handling for incoming clients."""

        while True:
            global client_address
            client, client_address = SERVER.accept()
            client.send(bytes("Introduce un nombre y pulsa enter ", "utf8"))

            client.send(bytes("Asegurate de borrar 'Escribe...' antes de enviar tu nombre", "utf8"))
            addresses[client] = client_address
            Thread(target=threads.handle_client, args=(client,)).start()




    def handle_client(client):  # Takes client socket as argument.
        """Handles a single client connection."""
        name = client.recv(BUFSIZ).decode("utf8")
        welcome = 'Bienvenido %s! Pulsa el botón "Salir" para salir. No pulses X.' % name
        client.send(bytes(welcome, "utf8"))
        msg = "%s se ha unido al chat!" % name
        broadcast(bytes(msg, "utf8"))
        clients[client] = name

        while True:
            msg = client.recv(BUFSIZ)
            if msg != bytes("{quit}", "utf8"):
                broadcast(msg, name+": ")

            else:
                client.send(bytes("{quit}", "utf8"))
                client.close()
                del clients[client]
                broadcast(bytes("%s se ha ido del chat." % name, "utf8"))
                break




class IP():

    def __init__(self):
        # Tkinter part for defining the IP of the server. This works smoothly.

        global ip1
        ip1 = 1

    def aceptar(self):
        global HOST
        global PORT
        HOST = self.dip.get() 
        PORT = self.port.get()
        if not PORT:
            PORT = 33000

        PORT = int(PORT)
        self.raiz.destroy()

        global host1
        host1 = 1


        global ADDR
        ADDR = (HOST, PORT)

        global SERVER
        SERVER = socket(AF_INET, SOCK_STREAM)
        SERVER.bind(ADDR)



    def borrar_mensa(self, evento):
        self.dip.set("")
        self.port.set("")



class main ():

        def __init__(self):



            def send(event=None):  # event is passed by binders.
                """Handles sending of messages."""
                msg1 = my_msg.get()
                my_msg.set("")  # Clears input field.
                msg_list.insert(tkinter.END, msg1)
                if msg1 == "{quit}":
                    client_socket.close()
                    top.quit()


            def on_closing(event=None):
                """This function is to be called when the window is closed."""
                my_msg.set("{quit}")
                send()


            top = tkinter.Tk()
            top.title("Servidor - Aristoi.................. V. 1.0")
            top.geometry("650x450")

            messages_frame = tkinter.Frame(top)
            my_msg = tkinter.StringVar()  # For the messages to be sent.
            my_msg.set("Introduzca función...")
            scrollbar = tkinter.Scrollbar(messages_frame)  # To navigate through past messages.

            # Following will contain the messages.
            global msg_list
            msg_list = tkinter.Listbox(messages_frame, height=23, width=95, yscrollcommand=scrollbar.set)

            #  Following will contain buttons functions.
            def SERVERclose():
                SERVER.close()

            def EXIT():
                top.destroy()

            def def_IP():
                if ip1 is None:
                    IP()
                else:
                    tkinter.messagebox.showerror(title="Error", message="IP ya definida. Reinicie")

            def listening():
                if host1 is None:
                    tkinter.messagebox.showerror(title="Error", message="No ha definido una IP para el servidor")

                else:
                    SERVER.listen(5)
                    INPUT = Thread(target=threads.accept_incoming_connections)
                    INPUT.start()
                    tkinter.messagebox.showinfo(title="Conectando", message="Esperando conexiones entrantes")
                    INPUT.join()
                    SERVER.close()
                    global host2
                    host2 = 1                             


            # BUTTONS
            separl = ttk.Separator(top, orient=HORIZONTAL)
            entry_field = tkinter.Entry(top, textvariable=my_msg, width = 95)
            entry_field.bind("<Return>", send)
            send_button = tkinter.Button(top, text="    Enviar    ", fg="black", command=send)
            salir = tkinter.Button(top, text="    Salir    ",bg="black", fg="white", command=EXIT)
            host = tkinter.Button(top, text="    IP    ",bg="black", fg="white", command=def_IP)
            connect = tkinter.Button(top, text="    Conectar    ",bg="black", fg="white", command=listening)
            servcls = tkinter.Button(top, text="    ServerCLose    ",bg="black", fg="white", command=SERVERclose)

            #STETICS

            scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y)
            msg_list.pack(side=tkinter.LEFT, fill=tkinter.BOTH)
            messages_frame.pack()
            entry_field.pack()
            send_button.pack()
            entry_field.place(x=29, y=380)
            send_button.place(x=29, y=410)
            salir.place(x=580, y=410)
            host.place(x=480, y = 410)
            connect.place(x=380, y = 410)
            servcls.place(x=280, y = 410)

            top.protocol("WM_DELETE_WINDOW", on_closing)
            top.mainloop()





clients = {}
addresses = {}

PORT = 33000
BUFSIZ = 1024

opt = int(0)
MK1 = 1
global host1
global host2
global ip1

host1 = None
host2 = None
ip1 = None


if __name__ == "__main__":
    main()

这是完整版的代码:

from socket import AF_INET, socket, SOCK_STREAM
from threading import Thread
from datetime import date
from tkinter import messagebox
from tkinter import *
from tkinter import ttk, font
from multiprocessing import Process


import tkinter
import getpass
import os
import time

global msgs
global opt

global ADDR
global MK1



def broadcast(msg, prefix=""):  # prefix is for name identification.
    """Broadcasts a message to all the clients."""

    for sock in clients:

        sock.send(bytes(prefix, "utf8")+msg)


class threads():



    def accept_incoming_connections():
        """Sets up handling for incoming clients."""

        while True:
            global client_address
            client, client_address = SERVER.accept()
            client.send(bytes("Introduce un nombre y pulsa enter ", "utf8"))

            client.send(bytes("Asegurate de borrar 'Escribe...' antes de enviar tu nombre", "utf8"))
            addresses[client] = client_address
            Thread(target=threads.handle_client, args=(client,)).start()




    def handle_client(client):  # Takes client socket as argument.
        """Handles a single client connection."""
        name = client.recv(BUFSIZ).decode("utf8")
        welcome = 'Bienvenido %s! Pulsa el botón "Salir" para salir. No pulses X.' % name
        client.send(bytes(welcome, "utf8"))
        msg = "%s se ha unido al chat!" % name
        broadcast(bytes(msg, "utf8"))
        clients[client] = name

        while True:
            msg = client.recv(BUFSIZ)
            if msg != bytes("{quit}", "utf8"):
                broadcast(msg, name+": ")

            else:
                client.send(bytes("{quit}", "utf8"))
                client.close()
                del clients[client]
                broadcast(bytes("%s se ha ido del chat." % name, "utf8"))
                break




class IP():

    def __init__(self):

        self.raiz = Tk()
        self.raiz.geometry("450x200")
        self.raiz.resizable(0,0)
        self.raiz.title("Ventana de direcciones")

        fuente = font.Font(weight="bold")

        self.etiq1 = ttk.Label(self.raiz, text="Dirección IP", font=fuente)
        self.etiq2 = ttk.Label(self.raiz, text="Puerto", font=fuente)

        self.dip = StringVar()
        self.port = StringVar()

        self.ctext1 = ttk.Entry(self.raiz, textvariable=self.dip , width=40)
        self.ctext2 = ttk.Entry(self.raiz, textvariable=self.port, width= 10)

        self.separ1 = ttk.Separator(self.raiz, orient=HORIZONTAL)

        self.boton1 = ttk.Button(self.raiz, text="Aceptar", command=self.aceptar)

        self.etiq1.place(x=30, y=40)
        self.etiq2.place(x=30, y=80)

        self.ctext1.place(x=150, y=42)
        self.ctext2.place(x=150, y=82)
        self.separ1.place(x=5, y=145, bordermode=OUTSIDE,height=10, width=420)

        self.boton1.place(x=170, y=160)

        self.ctext1.focus_set()

        self.ctext1.bind("<Button-1>", self.borrar_mensa)
        self.ctext2.bind("<Button-1>", self.borrar_mensa)
        global ip1
        ip1 = 1

    def aceptar(self):
        global HOST
        global PORT
        HOST = self.dip.get() 
        PORT = self.port.get()
        if not PORT:
            PORT = 33000

        PORT = int(PORT)
        self.raiz.destroy()

        global host1
        host1 = 1


        global ADDR
        ADDR = (HOST, PORT)

        global SERVER
        SERVER = socket(AF_INET, SOCK_STREAM)
        SERVER.bind(ADDR)



    def borrar_mensa(self, evento):
        self.dip.set("")
        self.port.set("")



class main ():

        def __init__(self):


        # accept_incoming_connections ---------------------------------------------------------------


            def host_log(self):
                msgs = "%s:%s se ha conectado a las " + current_time + " del día " + today
                msg_list.insert(tkinter.END, msgs)



        #  --------------------------------------------------------------------------------------------



        #Parte del Tkinter
        #-----------------------------------------


            def send(event=None):  # event is passed by binders.
                """Handles sending of messages."""
                msg1 = my_msg.get()
                my_msg.set("")  # Clears input field.
                msg_list.insert(tkinter.END, msg1)
                if msg1 == "{quit}":
                    client_socket.close()
                    top.quit()


            def on_closing(event=None):
                """This function is to be called when the window is closed."""
                my_msg.set("{quit}")
                send()


            top = tkinter.Tk()
            top.title("Servidor - Aristoi.................. V. 1.0")
            top.geometry("650x450")

            messages_frame = tkinter.Frame(top)
            my_msg = tkinter.StringVar()  # For the messages to be sent.
            my_msg.set("Introduzca función...")
            scrollbar = tkinter.Scrollbar(messages_frame)  # To navigate through past messages.

            # Following will contain the messages.
            global msg_list
            msg_list = tkinter.Listbox(messages_frame, height=23, width=95, yscrollcommand=scrollbar.set)

            def SERVERclose():
                SERVER.close()

            def EXIT():
                top.destroy()

            def def_IP():
                if ip1 is None:
                    IP()
                else:
                    tkinter.messagebox.showerror(title="Error", message="IP ya definida. Reinicie")

            def listening():
                if host1 is None:
                    tkinter.messagebox.showerror(title="Error", message="No ha definido una IP para el servidor")

                else:
                    SERVER.listen(5)
                    INPUT = Thread(target=threads.accept_incoming_connections)
                    INPUT.start()
                    tkinter.messagebox.showinfo(title="Conectando", message="Esperando conexiones entrantes")
                    INPUT.join()
                    SERVER.close()
                    global host2
                    host2 = 1                             


            # BOTONES
            separl = ttk.Separator(top, orient=HORIZONTAL)
            entry_field = tkinter.Entry(top, textvariable=my_msg, width = 95)
            entry_field.bind("<Return>", send)
            send_button = tkinter.Button(top, text="    Enviar    ", fg="black", command=send)
            salir = tkinter.Button(top, text="    Salir    ",bg="black", fg="white", command=EXIT)
            host = tkinter.Button(top, text="    IP    ",bg="black", fg="white", command=def_IP)
            connect = tkinter.Button(top, text="    Conectar    ",bg="black", fg="white", command=listening)
            servcls = tkinter.Button(top, text="    ServerCLose    ",bg="black", fg="white", command=SERVERclose)

            #ESTETICA

            scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y)
            msg_list.pack(side=tkinter.LEFT, fill=tkinter.BOTH)
            messages_frame.pack()
            entry_field.pack()
            send_button.pack()
            entry_field.place(x=29, y=380)
            send_button.place(x=29, y=410)
            salir.place(x=580, y=410)
            host.place(x=480, y = 410)
            connect.place(x=380, y = 410)
            servcls.place(x=280, y = 410)

            top.protocol("WM_DELETE_WINDOW", on_closing)
            top.mainloop()

        # AQUI




clients = {}
addresses = {}

PORT = 33000
BUFSIZ = 1024

opt = int(0)
MK1 = 1
global host1
global host2
global ip1

host1 = None
host2 = None
ip1 = None





t = time.localtime()
current_time = time.strftime("%H:%M:%S", t)
d1 = date.today()
today = d1.strftime("%d/%m/%Y")




if __name__ == "__main__":
    main()

【问题讨论】:

    标签: python multithreading tkinter python-multiprocessing


    【解决方案1】:

    它可能与谁有关

    我终于找到了解决问题的有效方法。我不必重写我的代码,我只是按照我在此link 上找到的模板对其进行了重新排序。归功于 Jacob Hallén。我将在下面留下代码的结构,以防您想快速浏览一下。

    import Tkinter
    import time
    import threading
    import random
    import Queue
    
    class GuiPart:
        def _ _init_ _(self, master, queue, endCommand):
            self.queue = queue
            # Set up the GUI
            console = Tkinter.Button(master, text='Done', command=endCommand)
            console.pack(  )
            # Add more GUI stuff here depending on your specific needs
    
        def processIncoming(self):
            """Handle all messages currently in the queue, if any."""
            while self.queue.qsize(  ):
                try:
                    msg = self.queue.get(0)
                    # Check contents of message and do whatever is needed. As a
                    # simple test, print it (in real life, you would
                    # suitably update the GUI's display in a richer fashion).
                    print msg
                except Queue.Empty:
                    # just on general principles, although we don't
                    # expect this branch to be taken in this case
                    pass
    
    class ThreadedClient:
        """
        Launch the main part of the GUI and the worker thread. periodicCall and
        endApplication could reside in the GUI part, but putting them here
        means that you have all the thread controls in a single place.
        """
        def _ _init_ _(self, master):
            """
            Start the GUI and the asynchronous threads. We are in the main
            (original) thread of the application, which will later be used by
            the GUI as well. We spawn a new thread for the worker (I/O).
            """
            self.master = master
    
            # Create the queue
            self.queue = Queue.Queue(  )
    
            # Set up the GUI part
            self.gui = GuiPart(master, self.queue, self.endApplication)
    
            # Set up the thread to do asynchronous I/O
            # More threads can also be created and used, if necessary
            self.running = 1
            self.thread1 = threading.Thread(target=self.workerThread1)
            self.thread1.start(  )
    
            # Start the periodic call in the GUI to check if the queue contains
            # anything
            self.periodicCall(  )
    
        def periodicCall(self):
            """
            Check every 200 ms if there is something new in the queue.
            """
            self.gui.processIncoming(  )
            if not self.running:
                # This is the brutal stop of the system. You may want to do
                # some cleanup before actually shutting it down.
                import sys
                sys.exit(1)
            self.master.after(200, self.periodicCall)
    
        def workerThread1(self):
            """
            This is where we handle the asynchronous I/O. For example, it may be
            a 'select(  )'. One important thing to remember is that the thread has
            to yield control pretty regularly, by select or otherwise.
            """
            while self.running:
                # To simulate asynchronous I/O, we create a random number at
                # random intervals. Replace the following two lines with the real
                # thing.
                time.sleep(rand.random(  ) * 1.5)
                msg = rand.random(  )
                self.queue.put(msg)
    
        def endApplication(self):
            self.running = 0
    
    rand = random.Random(  )
    root = Tkinter.Tk(  )
    
    client = ThreadedClient(root)
    root.mainloop(  )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-12-02
      • 1970-01-01
      • 2013-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多