main.py
from kivy.base import EventLoop
from kivy.config import Config
from kivy.graphics import Color, Line
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.widget import Widget
from kivy.utils import get_color_from_hex
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.clock import Clock
CURSOR = (
' @@@@ ',
' @--@ ',
' @--@ ',
' @--@ ',
' @--@ ',
' @@@@ ',
' ',
'@@@@@@ @@@@ @@@@@@ ',
'@----@ @--@ @----@ ',
'@----@ @--@ @----@ ',
'@@@@@@ @@@@ @@@@@@ ',
' ',
' @@@@ ',
' @--@ ',
' @--@ ',
' @--@ ',
' @--@ ',
' @@@@ ',
' ',
' ',
' ',
' ',
' ',
' ',
)
class Update_Location(Widget):
pass
class CanvasWidget(Widget):
line_width = 2
def on_touch_down(self, touch):
if Widget.on_touch_down(self, touch):
return
with self.canvas:
touch.ud['current_line'] = Line(
points=(touch.x, touch.y),
width=self.line_width)
def on_touch_move(self, touch):
if 'current_line' in touch.ud:
touch.ud['current_line'].points += (touch.x, touch.y)
def set_color(self, new_color):
self.last_color = new_color
self.canvas.add(Color(*new_color))
def set_line_width(self, line_width='Normal'):
self.line_width = {
'Thin': 1, 'Normal': 2, 'Thick': 4
}[line_width]
def clear_canvas(self):
saved = self.children[:]
self.clear_widgets()
self.canvas.clear()
for widget in saved:
self.add_widget(widget)
self.set_color(self.last_color)
def start_server(self):
host = '127.0.0.1'
port = 5000
notification_text="Server started on host: "+host+" and port: "+str(port)
server_start=Popup(title='Notification',content=Label(text=notification_text),size_hint=(.75,.75),auto_dismiss=True)
server_start.open()
Clock.schedule_interval(server_start.dismiss, 3)
class PaintApp(App):
def build(self):
EventLoop.ensure_window()
if EventLoop.window.__class__.__name__.endswith('Pygame'):
try:
from pygame import mouse
a, b = pygame_compile_cursor()
mouse.set_cursor((24, 24), (9, 9), a, b)
except:
pass
#boxlayout
self.layout = BoxLayout(orientation='vertical')
self.canvas_widget = CanvasWidget()
self.canvas_widget.set_color(
get_color_from_hex('#2980b9'))
self.layout.add_widget(self.canvas_widget)
#self.layout.add_widget(Label(text="Started Server : False , Connected to Server : False",color=(1,1,1),size_hint=(1, .1)))
return self.layout
#return self.canvas_widget
class RadioButton(ToggleButton):
def _do_press(self):
if self.state == 'normal':
ToggleButtonBehavior._do_press(self)
def pygame_compile_cursor(black='@', white='-'):
aa, bb = [], []
a = b = 0
i = 8
for s in CURSOR:
for c in s:
a <<= 1
b <<= 1
i -= 1
if c == black:
a |= 1
b |= 1
elif c == white:
b |= 1
if not i:
aa.append(a)
bb.append(b)
a = b = 0
i = 8
return tuple(aa), tuple(bb)
if __name__ == '__main__':
Config.set('graphics', 'width', '960')
Config.set('graphics', 'height', '540') # 16:9
# Config.set('graphics', 'resizable', '0')
# Config.set('input', 'mouse', 'mouse,disable_multitouch')
from kivy.core.window import Window
Window.clearcolor = get_color_from_hex('#ffffff')
PaintApp().run()
server.py
import socket
def start_server(host,port):
s = socket.socket()
s.bind((host,port))
s.listen(1)
c, addr = s.accept()
print "Connection from: " + str(addr)
while True:
data = c.recv(1024)
if not data:
break
print "from connected user: " + str(data)
data = str(data).upper()
print "sending: " + str(data)
c.send(data)
c.close()
if __name__ == '__main__':
start_server()
Above is the code for my simple paint application(adopted from kivy blue prints).
My aim is to start server on one application on localhost and then others connect to it. Those who can connected to it should get their canvas updated with one from the server they connected to. How can I send the data and update canvas. I have created a server in the above code using python's socket library.
I don't want to use http requests/threads instead establish connections with sockets and use async.io.
I created a quick REST flask service with a /canvas endpoint that supported post. I also created a lines member of the CanvasWidget class to save how to recreate the data to send it to the server. I had thought about doing pixel by pixel but I thought tracking the Line creation was more elegant and would support an undo redo feature if ever requested.
Directions
main.py
from kivy.app import App
from kivy.base import EventLoop
from kivy.config import Config
from kivy.graphics import Color, Line
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.widget import Widget
from kivy.utils import get_color_from_hex
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.clock import Clock
from kivy.network.urlrequest import UrlRequest
import flask
CURSOR = (
' @@@@ ',
' @--@ ',
' @--@ ',
' @--@ ',
' @--@ ',
' @@@@ ',
' ',
'@@@@@@ @@@@ @@@@@@ ',
'@----@ @--@ @----@ ',
'@----@ @--@ @----@ ',
'@@@@@@ @@@@ @@@@@@ ',
' ',
' @@@@ ',
' @--@ ',
' @--@ ',
' @--@ ',
' @--@ ',
' @@@@ ',
' ',
' ',
' ',
' ',
' ',
' ',
)
class Update_Location(Widget):
pass
class CanvasWidget(Widget):
line_width = 2
lines = []
def __init__(self, **kwargs):
super(CanvasWidget, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_down=self._on_keyboard_down)
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 's':
print 'send'
json = flask.json.dumps(self.lines)
req = UrlRequest('http://127.0.0.1:5000/canvas', req_body=json)
elif keycode[1] == 'r':
print 'receive'
req = UrlRequest('http://127.0.0.1:5000/canvas', self.load_state)
def on_touch_down(self, touch):
if Widget.on_touch_down(self, touch):
return
with self.canvas:
touch.ud['current_line'] = Line(
points=(touch.x, touch.y),
width=self.line_width)
self.lines.append({'points':[touch.x,touch.y], 'width':self.line_width})
def on_touch_move(self, touch):
if 'current_line' in touch.ud:
touch.ud['current_line'].points += (touch.x, touch.y)
self.lines[-1]['points'].append(touch.x)
self.lines[-1]['points'].append(touch.y)
def set_color(self, new_color):
self.last_color = new_color
self.canvas.add(Color(*new_color))
def set_line_width(self, line_width='Normal'):
self.line_width = {
'Thin': 1, 'Normal': 2, 'Thick': 4
}[line_width]
def clear_canvas(self):
saved = self.children[:]
self.clear_widgets()
self.canvas.clear()
for widget in saved:
self.add_widget(widget)
self.set_color(self.last_color)
def load_state(self, req, results):
print 'load_state req', repr(req)
print 'load_state results', repr(results)
#needs some validation here
self.clear_canvas()
with self.canvas:
for line in results:
Line(points=line['points'], width=line['width'])
self.line = results
def start_server(self):
host = '127.0.0.1'
port = 5000
notification_text="Server started on host: "+host+" and port: "+str(port)
server_start=Popup(title='Notification',content=Label(text=notification_text),size_hint=(.75,.75),auto_dismiss=True)
server_start.open()
Clock.schedule_interval(server_start.dismiss, 3)
class PaintApp(App):
def build(self):
EventLoop.ensure_window()
if EventLoop.window.__class__.__name__.endswith('Pygame'):
try:
from pygame import mouse
a, b = pygame_compile_cursor()
mouse.set_cursor((24, 24), (9, 9), a, b)
except:
pass
#boxlayout
self.layout = BoxLayout(orientation='vertical')
self.canvas_widget = CanvasWidget()
self.canvas_widget.set_color(
get_color_from_hex('#2980b9'))
self.layout.add_widget(self.canvas_widget)
#self.layout.add_widget(Label(text="Started Server : False , Connected to Server : False",color=(1,1,1),size_hint=(1, .1)))
return self.layout
#return self.canvas_widget
class RadioButton(ToggleButton):
def _do_press(self):
if self.state == 'normal':
ToggleButtonBehavior._do_press(self)
def pygame_compile_cursor(black='@', white='-'):
aa, bb = [], []
a = b = 0
i = 8
for s in CURSOR:
for c in s:
a <<= 1
b <<= 1
i -= 1
if c == black:
a |= 1
b |= 1
elif c == white:
b |= 1
if not i:
aa.append(a)
bb.append(b)
a = b = 0
i = 8
return tuple(aa), tuple(bb)
if __name__ == '__main__':
Config.set('graphics', 'width', '960')
Config.set('graphics', 'height', '540') # 16:9
# Config.set('graphics', 'resizable', '0')
# Config.set('input', 'mouse', 'mouse,disable_multitouch')
from kivy.core.window import Window
Window.clearcolor = get_color_from_hex('#ffffff')
PaintApp().run()
server.py
from flask import Flask, jsonify, request, json
app = Flask(__name__)
data = []
@app.route('/canvas', methods = ['GET', 'POST'])
def canvas():
global data
if request.method == 'GET':
tmp = jsonify({})
tmp.set_data(json.dumps(data))
return tmp
if request.method == 'POST':
data = json.loads(request.data)
tmp = jsonify({})
tmp.set_data(json.dumps(data))
return tmp
if __name__ == '__main__':
app.run(debug=True)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With