【发布时间】:2014-03-16 23:15:09
【问题描述】:
我使用这个论坛有一段时间了,但这是我第一次提出问题,因为我无法找到解决困难的好方法,并且因为我希望这个问题对我有用也给其他人。
我正在实现一个简单的通知板,即显示来自套接字连接的消息的窗口。该板以红色打印收到的最后一条消息,以蓝色打印旧消息,最多十个。当客户端发送的消息为'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() 的主要部分。
#!/usr/bin/env python
import socket
import threading
from Tkinter import *
from datetime import datetime
### Class definition
class NoticationsBoard() :
def __init__(self, title):
self.messages = []
self.toplevel = None
self.title = title
self.backgroundColor = 'black'
self.textColor = 'blue'
self.textColorFirst = 'red'
self.boardSize = '200x250+0+0'
self.listsize = 10
def createBoard(self):
self.toplevel = Toplevel()
self.toplevel.title(self.title)
self.toplevel.configure(background='black')
self.toplevel.geometry(self.boardSize)
def publish(self, message):
self.addToList(message)
self.displayList()
def addToList(self, msg):
if len(self.messages) == self.listsize:
self.messages.pop(0)
timestamp = datetime.utcnow().strftime('%H:%M:%S')
newMessage = (msg, timestamp)
self.messages.append(newMessage)
def displayList(self):
# Destroy and create the window (is it really necessary?)
if self.toplevel is not None :
self.toplevel.destroy()
self.createBoard()
# create labels for all the messages in the list
index = 1
for m, t in self.messages :
color = self.textColor
if index == len(self.messages) :
color = self.textColorFirst
label = Label(self.toplevel, text=m, height=0, width=100, fg=color, anchor=W)
label.grid(row=0,column=1)
label.configure(background=self.backgroundColor)
label.pack(side='bottom')
index = index +1
####### Run
def receiveMessages(newsboard) :
print '===== Inside receiveMessages ======'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
try:
s.bind((HOST, PORT))
except socket.error , msg:
print 'Bind failed. Error code: ' + str(msg[0]) + 'Error message: ' + msg[1]
sys.exit()
print 'Socket bind complete'
s.listen(1)
print 'Socket now listening on port', PORT
# Accept the connection once
(conn, addr) = s.accept()
print 'Connected with ' + addr[0] + ':' + str(addr[1])
stored_data = ''
while True:
# RECEIVE DATA
data = conn.recv(1024)
# PROCESS DATA
if data == 'Q' :
print 'Client wants to disconnect.'
reply = 'Q'
conn.send(reply)
break
else :
print data
newsboard.publish(data)
reply = 'Message received:' + data
conn.send(reply)
print 'Close connection.'
conn.close()
board.destroy()
HOST = '' # Symbolic name meaning the local host
PORT = 24073 # Arbitrary non-privileged port
app = Tk()
app.title("GUI main")
board = NoticationsBoard('Notifications')
t = threading.Thread(target=receiveMessages, args = (board,))
t.start()
app.update() # Not sure what it does and if it is necessary
app.mainloop()
我使用的是 Python 2.7.5。
最后,但这是次要的,我试图在消息本身的左侧以不同的颜色显示每条消息的时间戳。在我看来,在同一个标签上不可能有不同颜色的文本,所以我在 for 循环中创建了带有时间戳的其他标签。我尝试使用.grid(column=0) 和.grid(column=1) 将时间戳和消息标签一个挨一个地显示,但它们不是一个挨一个,而是一个在另一个之下,我还没弄清楚为什么。
如你所知,我不是一个熟练的程序员,而且绝对是 Python 的新手......
提前感谢谁会给我一些建议,希望这个问题对很多人有用。
【问题讨论】:
-
可能是 stackoverflow.com/q/16745507/291641 的副本。从不同的线程调用 UI 方法可能会导致问题。按照建议,使用队列并从 UI 线程轮询队列应该可以解决问题。或者使用 Twisted 以面向事件的方式使用 1 个线程进行整个运行。
-
感谢您的建议。我不确定如何避免从线程化的
receiveMessages()函数调用 UI 方法。我查看了您链接的问题,但我很难看到需要在哪里添加queue和poll。至于 Twisted,一个面向事件的工具可能是一个好主意,但我想知道这是否过于简单,因为理论上这段代码应该执行的任务很简单。在您看来,事件驱动的实现会简化整个结构吗? -
快速更新:我正在从 Jacob Hallén (code.activestate.com/recipes/…) 编写的配方开始重写代码。一旦我得到它的工作,我会上传我的工作解决方案(这可能不是最好的,但还没有!)。
标签: python multithreading sockets tkinter