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