Tkinter通知板带有来自套接字的消息怪异行为

2024-04-19 08:38:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我使用这个论坛已经有一段时间了,但这是我第一次问问题,因为我还没有找到一个好的方法来解决我的困难,因为我希望这个问题对其他人也有用。在

我正在实现一个简单的通知板,即一个窗口,显示来自套接字连接的消息。这个板用红色打印最后收到的信息,蓝色打印旧信息,最多10条。当客户端发送的消息为“Q”时,连接终止,通知板被销毁。在

我使用的是Tkinter、线程和套接字,但行为并不顺利(刷新通知板需要一段时间)。我可以想到几个问题:处理连接的线程没有关闭;窗口的更新是通过销毁并重新创建顶层来执行的。不幸的是,我不明白这些问题是否是问题的根源。在

以下是客户端的代码,非常简单:

#!/usr/bin/env python

import socket

HOST = ''           # Symbolic name meaning the local host
PORT = 24073        # Arbitrary non-privileged port

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST,PORT))

while True:
    message = raw_input('Enter your command (Q=quit): ')
    s.send(message)
    reply = s.recv(1024)
    if reply=='Q':
        print 'Request to disconnect was received.'
        break
    else :
        print reply
s.close()

这是服务器。服务器实现了一个处理通知板特性的类,一个用于套接字连接的线程,最后实现了带有mainloop()的主要部分。在

^{pr2}$

我使用的是python2.7.5。在

最后,这是一个次要的问题,我试图用不同的颜色在消息的左边显示每个消息的时间戳。在我看来,在同一个标签上不可能有不同颜色的文本,所以我在for循环中创建了其他带有时间戳的标签。我试图使用.grid(column=0).grid(column=1)将时间戳和消息标签一个挨一个地显示,但是它们不是一个挨着一个而是一个在另一个下面,我还不知道为什么。在

如你所知,我不是一个熟练的程序员,而且绝对是Python的新手。。。在

提前谢谢谁会给我一些建议,我希望这个问题对很多人有用。在


Tags: 服务器信息host消息客户端message颜色port
1条回答
网友
1楼 · 发布于 2024-04-19 08:38:59

好吧,似乎我通过采纳别人的问题、建议和代码找到了一个解决方案。外观上可能没有什么不同。 在GUI中,最值得注意的是,我预加载了所有标签,然后只修改了文本。 在穿线部分,这完全改变了。请看下面。在

#!/usr/local/bin/python

try:
    import Tkinter
except ImportError:
    import tkinter as Tkinter
import time
import threading
import random
import Queue
import socket
import sys
from datetime import datetime

class GuiPart:
    def __init__(self, master, queue):
        self.queue = queue
        # GUI stuff
        self.labelArray = []
        self.messages = []
        # Set up the GUI
        self.master = master
        self.backgroundColor = 'black'
        self.listsize = 10
        master.config(bg=self.backgroundColor)
        self.board = Tkinter.LabelFrame(self.master, text='Notification Board',     bg='Black', fg='Yellow', labelanchor='n', width=170)
        self.initLabels()
        self.board.pack()

    def initLabels(self) :
        self.textColorTime = 'cyan'
        self.textColorMessage = 'orange'
        colorTime = 'blue'
        colorMessage = 'red'
        for i in range(0,self.listsize):
            la = Tkinter.Label(self.board, height=0, width=10, bg=self.backgroundColor, fg=colorTime, anchor=Tkinter.W)
            lb = Tkinter.Label(self.board, height=0, width=160, bg=self.backgroundColor, fg=colorMessage)
            la.grid(row=i,column=0, sticky=Tkinter.W)
            lb.grid(row=i,column=1, sticky=Tkinter.W)
            self.labelArray.append((la, lb))
            colorTime = self.textColorTime
            colorMessage = self.textColorMessage
            self.initList()
        self.displayList()

    def initList(self):
        for i in range(0, self.listsize):
            t = ''
            m = ''
            self.messages.append((t,m))

    def processIncoming(self):
        while self.queue.qsize():
            try:
                msg = self.queue.get(0)
                self.processMessage(msg)
            except Queue.Empty:
                pass

    def processMessage(self, message):
        timestamp = datetime.utcnow().strftime('%H:%M:%S')
        self.publish(timestamp, message)

    def publish(self, msg1, msg2):
        self.addToList(msg1, msg2)
        self.displayList()

    def addToList(self, msg1, msg2):
        if len(self.messages) == self.listsize:
            self.messages.pop(0)
        if (msg1 == None):
            msg1 = datetime.utcnow().strftime('%H:%M:%S')
        newMessage = (msg1, msg2)
        self.messages.append(newMessage)

    def displayList(self):
        index = self.listsize -1
        for t, m in self.messages :
            la, lb = self.labelArray[index]
            la.config(text=t)
            lb.config(text=m)
            index = index -1

    def destroy(self):
        self.master.destroy()

class ThreadedClient:

    def __init__(self, master):
        self.master = master
        # Create the queue
        self.queue = Queue.Queue()
        # Define connection parameters
        self.conn = None
        self.bindError = False
        # Set up the GUI part
        self.gui = GuiPart(master, self.queue)
        # Set up the thread to do asynchronous I/O
        self.running = True
        self.commThread = threading.Thread(target=self.workerThreadReceive)
        self.commThread.daemon = True
        self.commThread.start()
        # Start the periodic call in the GUI to check if the queue contains anything
        self.periodicCall()

    def periodicCall(self):
        if not self.running:
            self.killApplication()
        else :
            self.gui.processIncoming()
            self.master.after(100, self.periodicCall)

    def workerThreadReceive(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        try :
            s.bind((HOST, PORT))
        except socket.error as msg :
            print 'Bind failed. Error code: ' + str(msg[0]) + ' Error message: ' + str(msg[1])
            self.running = False
            self.bindError = True
            return
        s.listen(1)
        (self.conn, self.addr) = s.accept()
        while self.running :
            data = self.conn.recv(1024)
            if data == 'Q' :
                self.conn.sendall('Q')
                self.running = False
            else :
                self.queue.put(data)
                reply = 'ACK'
                self.conn.sendall(reply)
        if self.conn is not None:
            self.conn.close()

    def killApplication(self):
        self.running = False
        if (self.conn is None) and (not self.bindError) :
            sfake = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sfake.connect((HOST,PORT))
            sfake.sendall('Q')
            sfake.close()
        self.gui.destroy()
        sys.exit()


HOST = ''       # Symbolic name meaning the local host
PORT = 24073    # Arbitrary non-privileged port

root = Tkinter.Tk()

client = ThreadedClient(root)

root.protocol("WM_DELETE_WINDOW", client.killApplication)
root.mainloop()

客户与问题中的客户相同。在

我不确定我的代码是否是最优雅的代码(好吧,让我们这样说吧。。。不是的!),但它似乎起到了作用。尽管如此,我还是希望得到你们的反馈,因为我相信我忽略了很多问题,而且事情本来可以用更简单的方式来完成。:)

相关问题 更多 >