Send/sync Variable/text Over Lan In Python 3
OK, I wanted to make an python application that allows me to send a message over LAN. Here's the code that 'works' 'locally' (imagine that I'm forming 2 of my fingers to make an '
Solution 1:
Here are several files that may help you with developing a messaging system for your LAN.
Simple_Server.py
#! /usr/bin/env python3
import socket, select
def main():
a = [socket.socket(socket.AF_INET, socket.SOCK_STREAM)] # socket array
a[0].bind(('', 8989))
a[0].listen(5)
while True:
for b inselect.select(a, [], [])[0]: # ready socketif b is a[0]:
a.append(b.accept()[0])
else:
try:
c = b.recv(1 << 12) # sent message
except socket.error:
b.shutdown(socket.SHUT_RDWR)
b.close()
a.remove(b)
else:
for d in (d for d in a[1:] if d isnot b): # message sink
d.sendall(c)
if __name__ == '__main__':
main()
MultichatClient.py
#! /usr/bin/env python3from safetkinter import *
from tkinter.constants import *
import socket
import sys
classMultichatClient(Frame):
after_handle = Nonedef__init__(self, master, remote_host):
super().__init__(master)
self.message_area = ScrolledText(self, width=81, height=21,
wrap=WORD, state=DISABLED)
self.message_area.grid(sticky=NSEW, columnspan=2)
self.send_area = Entry(self)
self.send_area.bind('<Return>', self.keyPressed)
self.send_area.grid(sticky=EW)
b = Button(self, text='Send', command=self.mouseClicked)
b.grid(row=1, column=1)
self.send_area.focus_set()
try:
self.remote = socket.create_connection((remote_host, 8989))
except socket.gaierror:
print('Could not find host {}.'.format(remote_host))
except socket.error:
print('Could not connect to host {}.'.format(remote_host))
else:
self.remote.setblocking(False)
self.after_handle = self.after_idle(self.dataready)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
@classmethoddefmain(cls, args):
root = Tk()
root.title('MultichatClient version 1.0')
m = cls(root, args[0])
m.grid(sticky=NSEW)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
root.mainloop()
return1defdataready(self):
try:
s = self.remote.recv(1 << 12).decode()
except socket.error:
passelse:
self.message_area['state'] = NORMAL
self.message_area.insert(END, s)
self.message_area['state'] = DISABLED
self.message_area.see(END)
self.after_handle = self.after(100, self.dataready)
defdestroy(self):
if self.after_handle:
self.after_cancel(self.after_handle)
super().destroy()
defmouseClicked(self, e=None):
self.remote.sendall(self.send_area.get().encode() + b'\r\n')
self.send_area.delete(0, END)
keyPressed = mouseClicked
if __name__ == '__main__':
sys.exit(MultichatClient.main(sys.argv[1:]))
Simple_Client.pyw
#! /usr/bin/env python3"""Provide a GUI for easy interactions with Multichat servers.
This program is an example of a first attempt at implementing a client
for interacting with a Multichat server through purely graphical means."""
__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower@gmail.com>'
__date__ = '11 October 2012'
__version__ = 1, 0, 0################################################################################from tkinter.messagebox import *
from tkinter.constants import *
from safetkinter import *
import logging
import traceback
import _thread
import socket
import os
import traceback
import sys
import threadbox
################################################################################classSimpleClient(Frame):
"SimpleClient(master, **kw) -> SimpleClient instance" @classmethoddefmain(cls):
"Create a GUI root and demonstrate the SimpleClient widget."
root = Tk()
root.title('Chat Client')
root.minsize(675, 450)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
root.bind_all('<Control-Key-a>', cls.handle_control_a)
frame = cls(root)
frame.grid(sticky=NSEW)
root.mainloop()
@staticmethoddefhandle_control_a(event):
"Process Ctrl-A commands by widget type."
widget = event.widget
ifisinstance(widget, Text):
widget.tag_add(SEL, 1.0, END + '-1c')
return'break'ifisinstance(widget, Entry):
widget.selection_range(0, END)
return'break'def__init__(self, master, **kw):
"Initialize the SimpleClient instance with the widgets it contains."super().__init__(master, **kw)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
# Build Widgets
self.output_area = ScrolledText(self, width=25, height=4, wrap=WORD)
self.input_area = Entry(self)
self.corner = Sizegrip(self)
# Place Widgets
self.output_area.grid(row=0, column=0, columnspan=2, sticky=NSEW)
self.input_area.grid(row=1, column=0, sticky=EW)
self.corner.grid(row=1, column=1, sticky=SE)
# Setup Widgets
self.output_area['state'] = DISABLED
self.input_area.bind('<Return>', self.send)
self.after_idle(self.connect)
defconnect(self):
"Try connecting to a server to begin chatting."
self.connection = Connector(self, 'Chat Client').connection
if self.connection isNone:
self._root().destroy()
else:
self.connection.setblocking(False)
self.after_idle(self.update)
defsend(self, event):
"Send a message across the connection from the given widget."
self.connection.sendall(event.widget.get().encode() + b'\r\n')
event.widget.delete(0, END)
defupdate(self):
"Update the output area with any incoming messages."
self.output_area['state'] = NORMAL
try:
self.output_area.insert(END, self.connection.recv(1 << 12).decode())
except socket.error:
passelse:
self.output_area.see(END)
finally:
self.output_area['state'] = DISABLED
self.after(100, self.update)
################################################################################defstart_thread(function, *args, **kwargs):
"Start a new thread of execution while logging any errors."
_thread.start_new_thread(log_errors, (function, args, kwargs))
deflog_errors(function, args=(), kwargs={}):
"Execute a function with its arguments and log any exceptions."try:
function(*args, **kwargs)
except SystemExit:
passexcept:
basename = os.path.basename(sys.argv[0])
filename = os.path.splitext(basename)[0] + '.log'
logging.basicConfig(filename=filename)
logging.error(traceback.format_exc())
################################################################################classDialog(Toplevel): # Copies tkinter.simpledialog.Dialog"Dialog(parent, title=None) -> Dialog instance"def__init__(self, parent, title=None):
"Initialize a Dialog window that takes focus away from the parent."super().__init__(parent)
self.withdraw()
if parent.winfo_viewable():
self.transient(parent)
if title:
self.title(title)
self.parent = parent
self.result = None
body = Frame(self)
self.initial_focus = self.body(body)
body.grid(sticky=NSEW, padx=5, pady=5)
self.buttonbox()
ifnot self.initial_focus:
self.initial_focus = self
self.protocol('WM_DELETE_WINDOW', self.cancel)
if self.parent isnotNone:
self.geometry('+{}+{}'.format(parent.winfo_rootx() + 50,
parent.winfo_rooty() + 50))
self.deiconify()
self.initial_focus.focus_set()
try:
self.wait_visibility()
except tkinter.TclError:
passelse:
self.grab_set()
self.wait_window(self)
defdestroy(self):
"Destruct the Dialog window."
self.initial_focus = Nonesuper().destroy()
defbody(self, master):
"Create the body of this Dialog window."passdefbuttonbox(self):
"Create the standard buttons and Dialog bindings."
box = Frame(self)
w = Button(box, text='OK', width=10, command=self.ok, default=ACTIVE)
w.grid(row=0, column=0, padx=5, pady=5)
w = Button(box, text='Cancel', width=10, command=self.cancel)
w.grid(row=0, column=1, padx=5, pady=5)
self.bind('<Return>', self.ok)
self.bind('<Escape>', self.cancel)
box.grid()
defok(self, event=None):
"Validate and apply the changes made by this Dialog."ifnot self.validate():
self.initial_focus.focus_set()
return
self.withdraw()
self.update_idletasks()
try:
self.apply()
finally:
self.cancel()
defcancel(self, event=None):
"Close the Dialong window and return to its parent."if self.parent isnotNone:
self.parent.focus_set()
self.destroy()
defvalidate(self):
"Verify that the Dialog is in a valid state."returnTruedefapply(self):
"Make any changes the Dialog wishes to accomplish."pass################################################################################classConnector(Dialog):
"Connector(parent, title=None) -> Connector instance"defbody(self, master):
"Customize the Dialog window with some custom widgets."
self.connection = None
self.resizable(False, False)
# Build Widgets
self.prompt = Label(master, text='Enter server IP address:')
self.address = Entry(master)
# Place Widgets
self.prompt.grid(sticky=W, padx=30, pady=2)
self.address.grid(sticky=W, padx=30)
defbuttonbox(self):
"Redefine the buttons at the bottom of the window."
w = Button(self, text='Connect', width=10, command=self.ok,
default=ACTIVE)
w.grid(sticky=E, padx=5, pady=5)
self.bind('<Return>', self.ok)
self.bind('<Escape>', self.cancel)
defvalidate(self):
"Ask a Consumator to make a connection with the given address."
c = Consumator(self, 'Chat Client', (self.address.get(), 8989))
if c.connection isNone:
Message(self, icon=WARNING, type=OK, title='Warning',
message='Could not connect to address!').show()
returnFalse
self.connection = c.connection
returnTrue################################################################################classConsumator(Dialog):
"Consumator(parent, title, address) -> Consumator instance"def__init__(self, parent, title, address):
"Initialize the Consumator with the server's address."
self.server_address = address
super().__init__(parent, title)
defbody(self, master):
"Create the widgets for this Dialog and start the connection process."
self.connection = None
self.resizable(False, False)
# Build Widgets
self.message = Label(master, text='Trying to connect to address ...')
self.progress = Progressbar(master, orient=HORIZONTAL)
# Place Widgets
self.message.grid(sticky=W, padx=10, pady=2)
self.progress.grid(sticky=EW, padx=10, pady=2)
# Setup Widgets
self.progress.configure(mode='indeterminate', maximum=30)
self.progress.start()
result = []
start_thread(self.connect, result)
self.after_idle(self.poll, result)
defbuttonbox(self):
"Cancel the creation of the buttons at the bottom of this Dialog."pass @threadbox.MetaBox.threaddefconnect(self, result):
"Try connecting to the server address that was given."try:
result.append(socket.create_connection(self.server_address, 10))
except socket.timeout:
result.append(None)
defpoll(self, result):
"Find out if the any connection information is available yet."if result:
self.connection = result[0]
self.cancel()
else:
self.after(100, self.poll, result)
################################################################################if __name__ == '__main__':
log_errors(SimpleClient.main)
affinity.py
"""Allow a simple way to ensure execution is confined to one thread.
This module defines the Affinity data type that runs code on a single thread.
An instance of the class will execute functions only on the thread that made
the object in the first place. The class is useful in a GUI's main loop."""
__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower@gmail.com>'
__date__ = '4 June 2012'
__version__ = 1, 0, 0################################################################################import sys
import _thread
import queue
################################################################################defslots(names=''):
"Sets the __slots__ variable in the calling context with private names."
sys._getframe(1).f_locals['__slots__'] = \
tuple('__' + name for name in names.replace(',', ' ').split())
################################################################################classAffinity:
"Affinity() -> Affinity instance"
slots('thread, action')
def__init__(self):
"Initializes instance with thread identity and job queue."
self.__thread = _thread.get_ident()
self.__action = queue.Queue()
def__call__(self, func, *args, **kwargs):
"Executes function on creating thread and returns result."if _thread.get_ident() == self.__thread:
whilenot self.__action.empty():
self.__action.get_nowait()()
return func(*args, **kwargs)
delegate = _Delegate(func, args, kwargs)
self.__action.put_nowait(delegate)
return delegate.value
################################################################################class_Delegate:
"_Delegate(func, args, kwargs) -> _Delegate instance"
slots('func, args, kwargs, mutex, value, error')
def__init__(self, func, args, kwargs):
"Initializes instance from arguments and prepares to run."
self.__func = func
self.__args = args
self.__kwargs = kwargs
self.__mutex = _thread.allocate_lock()
self.__mutex.acquire()
def__call__(self):
"Executes code with arguments and allows value retrieval."try:
self.__value = self.__func(*self.__args, **self.__kwargs)
self.__error = Falseexcept:
self.__value = sys.exc_info()[1]
self.__error = True
self.__mutex.release()
@propertydefvalue(self):
"Waits for value availability and raises or returns data."
self.__mutex.acquire()
if self.__error:
raise self.__value
return self.__value
threadbox.py
"""Provide a way to run instance methods on a single thread.
This module allows hierarchical classes to be cloned so that their instances
run on one thread. Method calls are automatically routed through a special
execution engine. This is helpful when building thread-safe GUI code."""
__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower@gmail.com>'
__date__ = '9 October 2012'
__version__ = 1, 0, 1################################################################################import functools
import affinity
################################################################################class_object: __slots__ = '_MetaBox__exec', '__dict__'################################################################################classMetaBox(type):
"MetaBox(name, bases, classdict, old=None) -> MetaBox instance"
__REGISTRY = {object: _object}
__SENTINEL = object()
@classmethoddefclone(cls, old, update=()):
"Creates a class preferring thread affinity after update."
classdict = dict(old.__dict__)
classdict.update(update)
return cls(old.__name__, old.__bases__, classdict, old)
@classmethoddefthread(cls, func):
"Marks a function to be completely threaded when running."
func.__thread = cls.__SENTINEL
return func
def__new__(cls, name, bases, classdict, old=None):
"Allocates space for a new class after altering its data."assert'__new__'notin classdict, '__new__ must not be defined!'assert'__slots__'notin classdict, '__slots__ must not be defined!'assert'__module__'in classdict, '__module__ must be defined!'
valid = []
for base in bases:
if base in cls.__REGISTRY:
valid.append(cls.__REGISTRY[base])
elif base in cls.__REGISTRY.values():
valid.append(base)
else:
valid.append(cls.clone(base))
for key, value in classdict.items():
ifcallable(value) and (nothasattr(value, '_MetaBox__thread') or
value.__thread isnot cls.__SENTINEL):
classdict[key] = cls.__wrap(value)
classdict.update({'__new__': cls.__new, '__slots__': (), '__module__':
'{}.{}'.format(__name__, classdict['__module__'])})
cls.__REGISTRY[object() if old isNoneelse old] = new = \
super().__new__(cls, name, tuple(valid), classdict)
return new
def__init__(self, name, bases, classdict, old=None):
"Initializes class instance while ignoring the old class."returnsuper().__init__(name, bases, classdict)
@staticmethoddef__wrap(func):
"Wraps a method so execution runs via an affinity engine." @functools.wraps(func)defbox(self, *args, **kwargs):
return self.__exec(func, self, *args, **kwargs)
return box
@classmethoddef__new(meta, cls, *args, **kwargs):
"Allocates space for instance and finds __exec attribute."
self = object.__new__(cls)
if'master'in kwargs:
self.__exec = kwargs['master'].__execelse:
valid = tuple(meta.__REGISTRY.values())
for value in args:
ifisinstance(value, valid):
self.__exec = value.__execbreakelse:
self.__exec = affinity.Affinity()
return self
safetkinter.py
"""Register tkinter classes with threadbox for immediate usage.
This module clones several classes from the tkinter library for use with
threads. Instances from these new classes should run on whatever thread
the root was created on. Child classes inherit the parent's safety."""
__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower@gmail.com>'
__date__ = '4 June 2012'
__version__ = 1, 0, 0
################################################################################
import time
import tkinter.filedialog
import tkinter.font
import tkinter.messagebox
import tkinter.scrolledtext
import tkinter.ttk
import threadbox
################################################################################
tkinter.NoDefaultRoot()
@threadbox.MetaBox.thread
def mainloop(self):
"Creates a synthetic main loop so that threads can still run."
while True:
try:
self.update()
except tkinter.TclError:
break
else:
time.sleep(tkinter._tkinter.getbusywaitinterval() / 1000)
threadbox.MetaBox.clone(tkinter.Misc, {'mainloop': mainloop})
################################################################################
OldButton = threadbox.MetaBox.clone(tkinter.Button)
Canvas = threadbox.MetaBox.clone(tkinter.Canvas)
OldFrame = threadbox.MetaBox.clone(tkinter.Frame)
Menu = threadbox.MetaBox.clone(tkinter.Menu)
PhotoImage = threadbox.MetaBox.clone(tkinter.PhotoImage)
Spinbox = threadbox.MetaBox.clone(tkinter.Spinbox)
StringVar = threadbox.MetaBox.clone(tkinter.StringVar)
Text = threadbox.MetaBox.clone(tkinter.Text)
Tk = threadbox.MetaBox.clone(tkinter.Tk)
Toplevel = threadbox.MetaBox.clone(tkinter.Toplevel)
################################################################################
Button = threadbox.MetaBox.clone(tkinter.ttk.Button)
Checkbutton = threadbox.MetaBox.clone(tkinter.ttk.Checkbutton)
Entry = threadbox.MetaBox.clone(tkinter.ttk.Entry)
Frame = threadbox.MetaBox.clone(tkinter.ttk.Frame)
Label = threadbox.MetaBox.clone(tkinter.ttk.Label)
Labelframe = threadbox.MetaBox.clone(tkinter.ttk.Labelframe)
Progressbar = threadbox.MetaBox.clone(tkinter.ttk.Progressbar)
Radiobutton = threadbox.MetaBox.clone(tkinter.ttk.Radiobutton)
Scale = threadbox.MetaBox.clone(tkinter.ttk.Scale)
Scrollbar = threadbox.MetaBox.clone(tkinter.ttk.Scrollbar)
Sizegrip = threadbox.MetaBox.clone(tkinter.ttk.Sizegrip)
Treeview = threadbox.MetaBox.clone(tkinter.ttk.Treeview)
################################################################################
Directory = threadbox.MetaBox.clone(tkinter.filedialog.Directory)
Font = threadbox.MetaBox.clone(tkinter.font.Font)
Message = threadbox.MetaBox.clone(tkinter.messagebox.Message)
ScrolledText = threadbox.MetaBox.clone(tkinter.scrolledtext.ScrolledText)
Post a Comment for "Send/sync Variable/text Over Lan In Python 3"