1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117 | """
A web app that allows multiple people to colaborate in painting. People
connecting later will not see the "paint" that was added earlier. Each
person is assigned a random color which affects how that person can
best contribute to the painting.
This app might be running at the demo server: http://flexx1.zoof.io
"""
import random
from flexx import app, ui, event
COLORS = ('#eee', '#999', '#555', '#111',
'#f00', '#0f0', '#00f', '#ff0', '#f0f', '#0ff',
'#a44', '#4a4', '#44a', '#aa4', '#afa', '#4aa',
)
class Relay(event.HasEvents):
""" Global object to relay paint events to all participants.
"""
@event.emitter
def global_paint(self, pos, color):
return dict(pos=pos, color=color)
# Create global relay
relay = Relay()
class ColabPainting(ui.Widget):
""" Web app for colaborative painting.
"""
CSS = """
.flx-ColabPainting { background: #ddd; }
.flx-ColabPainting .flx-CanvasWidget {
background: #fff;
border: 10px solid #000;
min-width: 400px; max-width: 400px;
min-height: 400px; max-height: 400px;
}
"""
def init(self):
# Select random color
self.color = random.choice(COLORS)
# App layout
with ui.VBox():
self.people = ui.Label(flex=0)
ui.Widget(flex=1)
with ui.HBox(flex=2):
ui.Widget(flex=1)
self.canvas = ui.CanvasWidget(flex=0)
ui.Widget(flex=1)
ui.Widget(flex=1)
# Start people-count-updater
self._update_participants()
@event.prop
def color(self, color='#000'):
""" The selected color for the current session. """
return str(color)
@event.connect('canvas.mouse_down')
def _this_user_adds_paint(self, *events):
""" Detect mouse down, emit global paint event via the relay. """
for ev in events:
relay.global_paint(ev.pos, self.color)
@relay.connect('global_paint') # note that we connect to relay here
def _any_user_adds_paint(self, *events):
""" Receive global paint event from the relay, emit local paint event. """
# if not self.session.status:
# return I think this is not required anymore. Worst case we get a warning
for ev in events:
self.emit('paint', ev)
def _update_participants(self):
""" Keep track of the number of participants. """
if not self.session.status:
return # and dont't invoke a new call
proxies = app.manager.get_connections(self.__class__.__name__)
n = len(proxies)
del proxies
self.people.text = '%i persons are painting' % n
app.call_later(3, self._update_participants)
class JS:
def init(self):
super().init()
self._ctx = self.canvas.node.getContext('2d')
@event.connect('color')
def _update_color(self, *events):
self.canvas.style = 'border: 10px solid ' + events[-1].new_value
@event.connect('!paint')
def _paint_dot(self, *events):
for ev in events:
self._ctx.globalAlpha = 0.8
self._ctx.beginPath()
self._ctx.fillStyle = ev.color
self._ctx.arc(ev.pos[0], ev.pos[1], 5, 0, 6.2831)
self._ctx.fill()
if __name__ == '__main__':
app.serve(ColabPainting)
m = app.launch(ColabPainting) # for use during development
app.start()
|