我正在使用这个论坛一段时间,但这是我第一次提出问题,因为我无法找到解决困难的好方法,因为我希望这个问题有用对其他人也是如此。
我正在实现一个简单的通知板,即一个显示来自套接字连接的消息的窗口。该板以红色打印最后收到的消息,蓝色打印旧的,最多十个。当客户端发送的消息为“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的新手......
先谢谢谁会给我一些建议,我希望这个问题对很多人有用。
答案 0 :(得分:0)
好吧,似乎我通过接受其他人的一些问题,建议和代码找到了解决方案。可能在外观上几乎没有差异。 在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()
客户端与问题中的相同。
我不确定我的代码是最优雅的代码(好吧,让它说出来......它不是!),但它似乎可以完成这项工作。不过,我想得到你的反馈意见,因为我确信我已经忽略了很多问题,并且事情本可以更容易地完成。 :)