【问题标题】:Show what the server is doing in tkinter在 tkinter 中显示服务器在做什么
【发布时间】:2017-11-20 12:10:35
【问题描述】:

我是 python 新手,在 tkinter 文本框中显示我的服务器的命令行输出时遇到问题。我想在 tkinter 文本框中显示正在发生的事情,并在列表框中显示 IP 地址列表。我看过书,但无法让它发挥作用。我正在使用 python 3.5。

这是我的代码:

from tkinter import *
import tkinter as tk
import threading
import socket
import time
import sys

from queue import Queue
import struct
import signal

NUMBER_OF_THREADS = 2
JOB_NUMBER = [1, 2]
queue = Queue()

COMMANDS = {'help':['Shows this help'],
            'list':['Lists connected clients'],
            'select':['Selects a client by its index. Takes index as a parameter'],
            'quit':['Stops current connection with a client. To be used when client is selected'],
            'shutdown':['Shuts server down'],
           }


class MultiServer(object):

    def __init__(self):
        self.host = ''
        self.port = 9999
        self.socket = None
        self.all_connections = []
        self.all_addresses = []

    def print_help(self):
        for cmd, v in COMMANDS.items():
            # print("{0}:\t{1}".format(cmd, v[0]))
            self.textBox.insert("{0}:\t{1}".format(cmd, v[0]))
        return

    def register_signal_handler(self):
        signal.signal(signal.SIGINT, self.quit_gracefully)
        signal.signal(signal.SIGTERM, self.quit_gracefully)
        return

    def quit_gracefully(self, signal=None, frame=None):
        print('\nQuitting gracefully')
        for conn in self.all_connections:
            try:
                conn.shutdown(2)
                conn.close()
            except Exception as e:
                print('Could not close connection %s' % str(e))
                # continue
        self.socket.close()
        sys.exit(0)

    def socket_create(self):
        try:
            self.socket = socket.socket()
        except socket.error as msg:
            print("Socket creation error: " + str(msg))
            # TODO: Added exit
            sys.exit(1)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        return

    def socket_bind(self):
        """ Bind socket to port and wait for connection from client """
        try:
            self.socket.bind((self.host, self.port))
            self.socket.listen(5)
        except socket.error as e:
            print("Socket binding error: " + str(e))
            time.sleep(5)
            self.socket_bind()
        return

    def accept_connections(self):
        """ Accept connections from multiple clients and save to list """
        for c in self.all_connections:
            c.close()
        self.all_connections = []
        self.all_addresses = []
        while 1:
            try:
                conn, address = self.socket.accept()
                conn.setblocking(1)
                client_hostname = conn.recv(1024).decode("utf-8")
                address = address + (client_hostname,)
            except Exception as e:
                print('Error accepting connections: %s' % str(e))
                # Loop indefinitely
                continue
            self.all_connections.append(conn)
            self.all_addresses.append(address)
            print('\nConnection has been established: {0} ({1})'.format(address[-1], address[0]))
        return

    def start_turtle(self):
        """ Interactive prompt for sending commands remotely """
        while True:
            cmd = input('turtle> ')
            if cmd == 'list':
                self.list_connections()
                continue
            elif 'select' in cmd:
                target, conn = self.get_target(cmd)
                if conn is not None:
                    self.send_target_commands(target, conn)
            elif cmd == 'shutdown':
                    queue.task_done()
                    queue.task_done()
                    print('Server shutdown')
                    break
                    # self.quit_gracefully()
            elif cmd == 'help':
                self.print_help()
            elif cmd == '':
                pass
            else:
                print('Command not recognized')
        return

    def list_connections(self):
        """ List all connections """
        results = ''
        for i, conn in enumerate(self.all_connections):
            try:
                conn.send(str.encode(' '))
                conn.recv(20480)
            except:
                del self.all_connections[i]
                del self.all_addresses[i]
                continue
            results += str(i) + '   ' + str(self.all_addresses[i][0]) + '   ' + str(
                self.all_addresses[i][1]) + '   ' + str(self.all_addresses[i][2]) + '\n'
        print('----- Clients -----' + '\n' + results)
        return

    def get_target(self, cmd):
        """ Select target client
        :param cmd:
        """
        target = cmd.split(' ')[-1]
        try:
            target = int(target)
        except:
            print('Client index should be an integer')
            return None, None
        try:
            conn = self.all_connections[target]
        except IndexError:
            print('Not a valid selection')
            return None, None
        print("You are now connected to " + str(self.all_addresses[target][2]))
        return target, conn

    def read_command_output(self, conn):
        """ Read message length and unpack it into an integer
        :param conn:
        """
        raw_msglen = self.recvall(conn, 4)
        if not raw_msglen:
            return None
        msglen = struct.unpack('>I', raw_msglen)[0]
        # Read the message data
        return self.recvall(conn, msglen)

    def recvall(self, conn, n):
        """ Helper function to recv n bytes or return None if EOF is hit
        :param n:
        :param conn:
        """
        # TODO: this can be a static method
        data = b''
        while len(data) < n:
            packet = conn.recv(n - len(data))
            if not packet:
                return None
            data += packet
        return data

    def send_target_commands(self, target, conn):
        """ Connect with remote target client
        :param conn:
        :param target:
        """
        conn.send(str.encode(" "))
        cwd_bytes = self.read_command_output(conn)
        cwd = str(cwd_bytes, "utf-8")
        print(cwd, end="")
        while True:
            try:
                cmd = input()
                if len(str.encode(cmd)) > 0:
                    conn.send(str.encode(cmd))
                    cmd_output = self.read_command_output(conn)
                    client_response = str(cmd_output, "utf-8")
                    print(client_response, end="")
                if cmd == 'quit':
                    break
            except Exception as e:
                print("Connection was lost %s" %str(e))
                break
        del self.all_connections[target]
        del self.all_addresses[target]
        return


def create_workers():
    """ Create worker threads (will die when main exits) """
    server = MultiServer()
    server.register_signal_handler()
    for _ in range(NUMBER_OF_THREADS):
        t = threading.Thread(target=work, args=(server,))
        t.daemon = True
        t.start()
    return


def work(server):
    """ Do the next job in the queue (thread for handling connections, another for sending commands)
    :param server:
    """
    while True:
        x = queue.get()
        if x == 1:
            server.socket_create()
            server.socket_bind()
            server.accept_connections()
        if x == 2:
            server.start_turtle()
        queue.task_done()
    return

def create_jobs():
    """ Each list item is a new job """
    for x in JOB_NUMBER:
        queue.put(x)
    queue.join()
    return

def main():
    create_workers()
    create_jobs()



class App(threading.Thread, MultiServer):

    def __init__(self):
        MultiServer.__init__(self)
        threading.Thread.__init__(self)
        self.start()


    def run(self):
        self.root = Tk()

        # create all of the main containers
        self.top_frame = Frame()
        self.center_frame = Frame()

        # layout all of the main containers
        self.root.grid_rowconfigure(1, weight=1)
        self.root.grid_columnconfigure(0, weight=1)

        self.top_frame.grid(row=0, sticky='NSEW')
        self.center_frame.grid(row=1, pady=5, sticky='NSEW')

        # Top frame
        self.logo = Label(self.top_frame, text='SunGroup')
        self.logo.grid(row=0, columnspan=3)

        self.command = Entry(self.top_frame, width=25)
        self.command.grid(row=1, column=0, padx=5)

        self.addButton = Button(self.top_frame, text='Enter', width=5)
        self.addButton.grid(row=1, column=1, padx=5)

        self.listButton = Button(self.top_frame, text='List', width=5, command=self.list_connections)
        self.listButton.grid(row=1, column=2)

        self.selectButton = Button(self.top_frame, text='Select', width=5, command=self.get_target)
        self.selectButton.grid(row=1, column=3)

        self.quitButton = Button(self.top_frame, text='Quit', width=5, command=self.quit_gracefully)
        self.quitButton.grid(row=1, column=4)

        self.helpButton = Button(self.top_frame, text='Help', width=5, command=self.print_help)
        self.helpButton.grid(row=1, column=5)


        # Center frame
        self.listbox = Listbox(self.center_frame, width=20, height=20)
        self.listbox.grid(row=0, column=0, padx=5)

        self.textBox = Text(self.center_frame, width=35, height=20)
        self.textBox.grid(row=0, column=1, padx=5)
        self.textBox.insert(END, self.start_turtle, self.print_help)


        self.root.mainloop()



if __name__ == '__main__':
    app = App()
    main()

【问题讨论】:

  • SO不是个人调试服务,尝试发minimal reproducible example
  • 而不是冒充你的所有代码。只需发布处理您的特定问题所需的最低金额。您只需要一个 tk 窗口、一个文本框,也许还需要一个按钮和一个输入框。其余的只是在路上。然后给出一些你尝试过的例子,我们可以使用它。

标签: python multithreading python-3.x tkinter


【解决方案1】:

当我运行你的代码时,我得到:

turtle> Tcl_WaitForEvent: Notifier not initialized
Abort

然后 Python 退出。如果这也是你的问题,那么this post 讨论了在 Tk 处于活动状态时在主线程之外调用input() 的问题,这会产生这个错误。解决方法似乎是:

cmd = input('turtle> ')

做一些类似的事情:

print('turtle>', end=' ')
sys.stdout.flush()
cmd = sys.stdin.readline().rstrip()

这更进一步,但我不能说事情然后顺利进行。您的客户端/服务器代码是否可以在没有 Tk 的情况下工作?当我尝试使用 Tk 或完全剥离 Tk 时,如果有活动的客户端连接,当我执行 list 时,它似乎会挂起。当我退出我的客户时,它会恢复。这可能是我测试方式的产物,但在尝试将接口分层之前,您的代码必须基本工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-10
    • 1970-01-01
    • 2014-03-18
    • 1970-01-01
    • 1970-01-01
    • 2017-04-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多