Added basic chat example.
This commit is contained in:
		
							
								
								
									
										26
									
								
								examples/chat app/client/client.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								examples/chat app/client/client.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | """ client communication for this simple chat example application. | ||||||
|  | This is a class derived from enetcomponents.client.client, which sends all data over pubsub via the topic response. | ||||||
|  | """ | ||||||
|  | from enetcomponents import client | ||||||
|  | from pubsub import pub | ||||||
|  |  | ||||||
|  | class client(client.client): | ||||||
|  |  | ||||||
|  | 	def network(self, event, data): | ||||||
|  | 		""" This functions receives data from an enet server in the following protocol: | ||||||
|  | 		dict(action="some_command", **kwargs) | ||||||
|  | 		This function will send all data to whatever listener in the pubsub stack by using the topic "response". | ||||||
|  | 	""" | ||||||
|  | 		f = data.get("action") | ||||||
|  | 		if f == None: | ||||||
|  | 			print("Error: Invalid data in protocol. %r" % (data)) | ||||||
|  | 			return | ||||||
|  | 		pub.sendMessage("response", data=data) | ||||||
|  |  | ||||||
|  | 	def connected(self, peer): | ||||||
|  | 		pub.sendMessage("ask_login") | ||||||
|  |  | ||||||
|  | 	def disconnected(self, peer): | ||||||
|  | 		pub.sendMessage("disconnected") | ||||||
|  |  | ||||||
							
								
								
									
										90
									
								
								examples/chat app/client/gui.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								examples/chat app/client/gui.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | """ Dfinition of all GUI components. """ | ||||||
|  | import wx | ||||||
|  |  | ||||||
|  | class loginDialog(wx.Dialog): | ||||||
|  |  | ||||||
|  | 	def __init__(self, title="Login"): | ||||||
|  | 		super(loginDialog, self).__init__(parent=None, id=wx.ID_ANY) | ||||||
|  | 		self.SetTitle(title) | ||||||
|  | 		panel = wx.Panel(self) | ||||||
|  | 		sizer = wx.BoxSizer(wx.VERTICAL) | ||||||
|  | 		label1 = wx.StaticText(panel, wx.ID_ANY, "Username: ") | ||||||
|  | 		self.username = wx.TextCtrl(panel, wx.ID_ANY, size=(380, -1)) | ||||||
|  | 		s = wx.BoxSizer(wx.HORIZONTAL) | ||||||
|  | 		s.Add(label1, 0, wx.ALL, 5) | ||||||
|  | 		s.Add(self.username, 0, wx.ALL, 5) | ||||||
|  | 		sizer.Add(s, 0, wx.ALL, 5) | ||||||
|  | 		label1 = wx.StaticText(panel, wx.ID_ANY, "Server: ") | ||||||
|  | 		self.server = wx.TextCtrl(panel, wx.ID_ANY, "localhost", size=(380, -1)) | ||||||
|  | 		s = wx.BoxSizer(wx.HORIZONTAL) | ||||||
|  | 		s.Add(label1, 0, wx.ALL, 5) | ||||||
|  | 		s.Add(self.server, 0, wx.ALL, 5) | ||||||
|  | 		sizer.Add(s, 0, wx.ALL, 5) | ||||||
|  | 		label1 = wx.StaticText(panel, wx.ID_ANY, "Port: ") | ||||||
|  | 		self.port = wx.SpinCtrl(panel, wx.ID_ANY, size=(380, -1)) | ||||||
|  | 		self.port.SetRange(1024, 65000) | ||||||
|  | 		self.port.SetValue(33333) | ||||||
|  | 		s = wx.BoxSizer(wx.HORIZONTAL) | ||||||
|  | 		s.Add(label1, 0, wx.ALL, 5) | ||||||
|  | 		s.Add(self.port, 0, wx.ALL, 5) | ||||||
|  | 		sizer.Add(s, 0, wx.ALL, 5) | ||||||
|  |  | ||||||
|  | #		label2 = wx.StaticText(panel, wx.ID_ANY, "Password: ") | ||||||
|  | #		self.password = wx.TextCtrl(panel, wx.ID_ANY, size=(380, -1), style=wx.TE_PASSWORD) | ||||||
|  | #		ss = wx.BoxSizer(wx.HORIZONTAL) | ||||||
|  | #		ss.Add(label2, 0, wx.ALL, 5) | ||||||
|  | #		ss.Add(self.password, 0, wx.ALL, 5) | ||||||
|  | #		sizer.Add(ss, 0, wx.ALL, 5) | ||||||
|  | 		ok = wx.Button(panel, wx.ID_OK, "Log in") | ||||||
|  | 		ok.SetDefault() | ||||||
|  | 		cancel = wx.Button(panel, wx.ID_CANCEL) | ||||||
|  | 		self.SetEscapeId(wx.ID_CANCEL) | ||||||
|  | 		bs = wx.BoxSizer(wx.HORIZONTAL) | ||||||
|  | 		bs.Add(ok, 0, wx.ALL, 5) | ||||||
|  | 		bs.Add(cancel, 0, wx.ALL, 5) | ||||||
|  | 		sizer.Add(bs, 0, wx.ALL, 5) | ||||||
|  | 		panel.SetSizer(sizer) | ||||||
|  | 		self.SetClientSize(sizer.CalcMin()) | ||||||
|  |  | ||||||
|  | class appFrame(wx.Frame): | ||||||
|  | 	def __init__(self): | ||||||
|  | 		super(appFrame, self).__init__(parent=None, title="Chat Window") | ||||||
|  | 		self.Maximize(True) | ||||||
|  | 		self.panel = wx.Panel(self) | ||||||
|  | 		self.sizer = wx.BoxSizer(wx.VERTICAL) | ||||||
|  | 		self.sb = self.CreateStatusBar() | ||||||
|  | 		lbl = wx.StaticText(self.panel, wx.ID_ANY, "menu") | ||||||
|  | 		self.list = wx.ListBox(self.panel, wx.ID_ANY) | ||||||
|  | 		self.sizer.Add(lbl, 0, wx.GROW) | ||||||
|  | 		self.sizer.Add(self.list, 1, wx.GROW) | ||||||
|  | 		lbl = wx.StaticText(self.panel, -1, "Chat") | ||||||
|  | 		self.chat = wx.TextCtrl(self.panel, -1) | ||||||
|  | 		sizerchat = wx.BoxSizer(wx.HORIZONTAL) | ||||||
|  | 		sizerchat.Add(lbl, 0, wx.ALL, 5) | ||||||
|  | 		sizerchat.Add(self.chat, 0, wx.ALL, 5) | ||||||
|  | 		self.sizer.Add(sizerchat, 0, wx.ALL, 5) | ||||||
|  | 		lbl1 = wx.StaticText(self.panel, wx.ID_ANY, "History") | ||||||
|  | 		self.history = wx.TextCtrl(self.panel, wx.ID_ANY, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(500, 300)) | ||||||
|  | 		box = wx.BoxSizer(wx.HORIZONTAL) | ||||||
|  | 		box.Add(lbl1, 0, wx.ALL, 5) | ||||||
|  | 		box.Add(self.history, 0, wx.ALL, 5) | ||||||
|  | 		self.sizer.Add(box, 0, wx.ALL, 5) | ||||||
|  | 		self.panel.SetSizerAndFit(self.sizer) | ||||||
|  |  | ||||||
|  | 	def get_item(self): | ||||||
|  | 		return self.list.GetSelection() | ||||||
|  |  | ||||||
|  | 	def add_message(self, message, reverse=False): | ||||||
|  | 		old_line = self.history.GetNumberOfLines() | ||||||
|  | 		point = self.history.GetInsertionPoint() | ||||||
|  | 		if reverse: | ||||||
|  | 			self.history.SetValue(message+"\n"+self.history.GetValue()) | ||||||
|  | 		else: | ||||||
|  | 			self.history.AppendText(message+"\n") | ||||||
|  | 		self.history.SetInsertionPoint(point) | ||||||
|  | 		new_line = self.history.GetNumberOfLines()#.count("\n") | ||||||
|  | 		return (old_line, new_line) | ||||||
|  |  | ||||||
|  | 	def  show_connection_error(self): | ||||||
|  | 		msg = wx.MessageDialog(None, "Connection error. Try again", "error", style=wx.ICON_ERROR).ShowModal() | ||||||
							
								
								
									
										91
									
								
								examples/chat app/client/main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								examples/chat app/client/main.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | import sys | ||||||
|  | import threading | ||||||
|  | import wx | ||||||
|  | import client | ||||||
|  | import output | ||||||
|  | import gui | ||||||
|  | from pubsub import pub | ||||||
|  |  | ||||||
|  | # Client instance. | ||||||
|  | c = None | ||||||
|  |  | ||||||
|  | # thread to keep the client running. | ||||||
|  | t = None | ||||||
|  |  | ||||||
|  | class controller(object): | ||||||
|  | 	def __init__(self, window): | ||||||
|  | 		super(controller, self).__init__() | ||||||
|  | 		self.window = window | ||||||
|  | 		self.connect_events() | ||||||
|  | 		self.window.Show() | ||||||
|  |  | ||||||
|  | 	def connect_events(self): | ||||||
|  | 		self.window.chat.Bind(wx.EVT_CHAR_HOOK, self.on_process) | ||||||
|  | 		pub.subscribe(self.response, "response") | ||||||
|  | 		pub.subscribe(self.ask_login, "ask_login") | ||||||
|  | 		pub.subscribe(self.disconnected, "disconnected") | ||||||
|  |  | ||||||
|  | 	def on_process(self, event): | ||||||
|  | 		key = event.GetKeyCode() | ||||||
|  | 		if key == wx.WXK_RETURN: | ||||||
|  | 			self.send_message() | ||||||
|  | 		event.Skip() | ||||||
|  |  | ||||||
|  | 	def send_message(self): | ||||||
|  | 		global c | ||||||
|  | 		message = self.window.chat.GetValue() | ||||||
|  | 		if message == "" or message == None: | ||||||
|  | 			return wx.Bell() | ||||||
|  | 		# Otherwise, message does exist. | ||||||
|  | 		data = dict(action="send_message", message=message) | ||||||
|  | 		c.send_data(0, data) | ||||||
|  | 		self.window.chat.ChangeValue("") | ||||||
|  |  | ||||||
|  | 	def response(self, data): | ||||||
|  | 		command = data.get("action") | ||||||
|  | 		if hasattr(self, "cmd_"+command): | ||||||
|  | 			getattr(self, "cmd_"+command)(data) | ||||||
|  |  | ||||||
|  | 	def cmd_connected(self, data): | ||||||
|  | 		connected = data.get("nickname") | ||||||
|  | 		msg = "{} has entered this platform".format(connected) | ||||||
|  | 		self.window.add_message(msg) | ||||||
|  |  | ||||||
|  | 	def cmd_message(self, data): | ||||||
|  | 		msg = data.get("message") | ||||||
|  | 		nickname = data.get("nickname") | ||||||
|  | 		msg = "{0}: {1}".format(nickname, msg) | ||||||
|  | 		output.speak(msg) | ||||||
|  | 		self.window.add_message(msg) | ||||||
|  |  | ||||||
|  | 	def ask_login(self): | ||||||
|  | 		global c | ||||||
|  | 		data = dict(action="login", nickname=self.username) | ||||||
|  | 		c.send_data(0, data) | ||||||
|  |  | ||||||
|  | 	def disconnected(self): | ||||||
|  | 		self.window.show_connection_error() | ||||||
|  | 		wx.GetApp().ExitMainLoop() | ||||||
|  |  | ||||||
|  | def setup(): | ||||||
|  | 	global c, t | ||||||
|  | 	output.setup() | ||||||
|  | 	app = wx.App() | ||||||
|  | 	d = gui.loginDialog() | ||||||
|  | 	f = gui.appFrame() | ||||||
|  | 	mainController = controller(f) | ||||||
|  | 	if d.ShowModal() != wx.ID_OK: | ||||||
|  | 		return | ||||||
|  | 	username = d.username.GetValue() | ||||||
|  | 	server = bytes(d.server.GetValue(), "utf-8") | ||||||
|  | 	port = int(d.port.GetValue()) | ||||||
|  | 	mainController.username = username | ||||||
|  | 	d.Destroy() | ||||||
|  | 	c = client.client(host=server, port=port) | ||||||
|  | 	t = threading.Thread(target=c.run) | ||||||
|  | 	t.start() | ||||||
|  | 	app.MainLoop() | ||||||
|  | 	c.close() | ||||||
|  |  | ||||||
|  | setup() | ||||||
							
								
								
									
										31
									
								
								examples/chat app/client/output.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								examples/chat app/client/output.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | # *- coding: utf-8 -*- | ||||||
|  | """ Simple (really simple) module to provide text to speech output in a chat window.""" | ||||||
|  | import logging as original_logging | ||||||
|  | logger = original_logging.getLogger('core.output') | ||||||
|  | from accessible_output2 import outputs | ||||||
|  | import sys | ||||||
|  |  | ||||||
|  | speaker = None | ||||||
|  | retries = 0 | ||||||
|  |  | ||||||
|  | def speak(text, interrupt=0): | ||||||
|  | 	global speaker, retries | ||||||
|  | 	if not speaker: | ||||||
|  | 		setup() | ||||||
|  | 	try: | ||||||
|  | 		speaker.speak(text, interrupt) | ||||||
|  | 	except: | ||||||
|  | 		if retries < 5: | ||||||
|  | 			retries = retries + 1 | ||||||
|  | 			speak(text) | ||||||
|  |  | ||||||
|  | def setup (): | ||||||
|  | 	global speaker | ||||||
|  | 	logger.debug("Initializing output subsystem.") | ||||||
|  | 	try: | ||||||
|  | 		speaker = outputs.auto.Auto() | ||||||
|  | 	except: | ||||||
|  | 		logger.exception("Output: Error during initialization.") | ||||||
|  |  | ||||||
|  | def enable_sapi(): | ||||||
|  | 	speaker = outputs.sapi.SAPI5() | ||||||
							
								
								
									
										7
									
								
								examples/chat app/requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/chat app/requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | pyenet | ||||||
|  | wxpython==4.0.3 | ||||||
|  | pypubsub | ||||||
|  | pywin32 | ||||||
|  | git+https://code.manuelcortez.net/manuelcortez/libloader | ||||||
|  | git+https://code.manuelcortez.net/manuelcortez/platform_utils | ||||||
|  | git+https://code.manuelcortez.net/manuelcortez/accessible_output2 | ||||||
							
								
								
									
										51
									
								
								examples/chat app/server/server.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								examples/chat app/server/server.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | from enetcomponents import server | ||||||
|  |  | ||||||
|  | class channel(server.channel): | ||||||
|  |  | ||||||
|  | 	def __init__(self, *args, **kwargs): | ||||||
|  | 		super(channel, self).__init__(*args, **kwargs) | ||||||
|  | 		self.nickname = None | ||||||
|  |  | ||||||
|  | 	def network(self, event, data): | ||||||
|  | 		f = data.get("action") | ||||||
|  | 		if f == None: | ||||||
|  | 			print("Error: Invalid data in protocol. %r" % (data)) | ||||||
|  | 			return | ||||||
|  | 		if hasattr(self, "cmd_"+f) == False: | ||||||
|  | 			print("Error: function cmd_{} does not exist".format(f)) | ||||||
|  | 			return | ||||||
|  | 		getattr(self, "cmd_"+f)(data) | ||||||
|  |  | ||||||
|  | 	def cmd_login(self, data): | ||||||
|  | 		nickname = data.get("nickname") | ||||||
|  | 		self.nickname = nickname | ||||||
|  | 		self.room = "public" | ||||||
|  | 		d = dict(action="connected", nickname=nickname) | ||||||
|  | 		self.server.send_to_all(0, d) | ||||||
|  |  | ||||||
|  | 	def cmd_send_message(self, data): | ||||||
|  | 		data.update(nickname=self.nickname, action="message") | ||||||
|  | 		for channel in self.server.peers: | ||||||
|  | 			if channel.room == self.room: | ||||||
|  | 				channel.send_data(0, data) | ||||||
|  |  | ||||||
|  | class server(server.server): | ||||||
|  |  | ||||||
|  | 	def __init__(self, *args, **kwargs): | ||||||
|  | 		super(server, self).__init__(*args, **kwargs) | ||||||
|  | 		self.rooms = list() | ||||||
|  |  | ||||||
|  | 	def connected(self, peer): | ||||||
|  | 		p = channel(self, peer) | ||||||
|  | 		self.peers[p] = True | ||||||
|  |  | ||||||
|  | 	def disconnected(self, peer): | ||||||
|  | 		for channel in self.peers: | ||||||
|  | 			if peer.incomingPeerID == channel.peer.incomingPeerID: | ||||||
|  | 				del self.peers[channel] | ||||||
|  | 				break | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  | 	print("Starting chat server...") | ||||||
|  | 	s = server() | ||||||
|  | 	s.run() | ||||||
		Reference in New Issue
	
	Block a user