Skip to content

Commit cced105

Browse files
authored
Add example that shows dragging (#121)
1 parent 9e25c73 commit cced105

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed

examples/drag.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"""
2+
Drag
3+
----
4+
5+
An example that shows coloured squares that can be dragged around using
6+
the pointer device. Hit space to add a new block, or escape to reset
7+
the blocks to their initial state.
8+
9+
This example is also useful for testing and developing backends and
10+
events, since any unintended delays (in the presentation of the bitmap,
11+
or the delivery of events) are easily felt as lag.
12+
"""
13+
14+
import numpy as np
15+
from rendercanvas.auto import RenderCanvas, loop
16+
17+
18+
canvas = RenderCanvas(present_method=None, update_mode="continuous")
19+
20+
context = canvas.get_context("bitmap")
21+
22+
23+
# The size of the blocks: hw is half the block width
24+
block_size = 100
25+
26+
# Define blocks to show: [x, y, size, rgba]
27+
initial_blocks = [
28+
[110, 110, block_size, (255, 0, 0, 255)],
29+
[220, 110, block_size, (0, 255, 0, 255)],
30+
[330, 110, block_size, (0, 0, 255, 255)],
31+
[110, 220, block_size, (255, 255, 0, 255)],
32+
[220, 220, block_size, (0, 255, 255, 255)],
33+
[330, 220, block_size, (255, 0, 255, 255)],
34+
]
35+
36+
# Make a copy, so we can reset the initial blocks
37+
blocks = [block.copy() for block in initial_blocks]
38+
39+
40+
# Dragging info. If not None it is (block_index, block_start_pos, pointer_start_pos)
41+
dragging = None
42+
43+
# The bitmap
44+
world = np.zeros((100, 100, 4), np.uint8)
45+
46+
47+
@canvas.add_event_handler("resize")
48+
def on_resize(event):
49+
# Resize the bitmap with the canvas, but in logical (not physical) pixels
50+
51+
global world
52+
w, h = int(event["width"]), int(event["height"])
53+
world = np.zeros((h, w, 4), np.uint8)
54+
55+
56+
@canvas.add_event_handler("pointer_down")
57+
def on_pointer_down(event):
58+
# Detect a drag start.
59+
# Note how we iterate over the blocks in reversed order, so it matches with
60+
# the fact that later blocks are drawn over earlier blocks.
61+
62+
global dragging
63+
hs = block_size // 2 # half-size
64+
65+
x, y = event["x"], event["y"]
66+
if event["button"] == 1:
67+
dragging = None
68+
for i in reversed(range(len(blocks))):
69+
block = blocks[i]
70+
bx, by = block[:2]
71+
if bx - hs < x < bx + hs and by - hs < y < by + hs:
72+
dragging = i, (bx, by), (x, y)
73+
break
74+
75+
76+
@canvas.add_event_handler("pointer_move")
77+
def on_pointer_move(event):
78+
# If we're dragging a block, update it's position.
79+
# We have stored the start position of the pointer and the block.
80+
# That way we can easily calculate the delta position for the block,
81+
# and even cancel the drag if we want
82+
83+
x, y = event["x"], event["y"]
84+
hs = block_size // 2
85+
86+
if dragging is not None:
87+
i, (bx, by), (rx, ry) = dragging
88+
89+
# Calculate delta pos, from the current pointer pos and the reference
90+
dx, dy = x - rx, y - ry
91+
92+
# Then apply the delta
93+
new_x = int(bx + dx)
94+
new_y = int(by + dy)
95+
96+
# Update the block position, while applying limits
97+
98+
block = blocks[i]
99+
block[0] = min(max(new_x, hs), world.shape[1] - hs)
100+
block[1] = min(max(new_y, hs), world.shape[0] - hs)
101+
102+
103+
@canvas.add_event_handler("pointer_up")
104+
def on_pointer_up(event):
105+
# Stop the drag action
106+
107+
global dragging
108+
if event["button"] == 1:
109+
dragging = None
110+
111+
112+
@canvas.add_event_handler("key_down")
113+
def on_key(event):
114+
key = event["key"]
115+
if key == "Escape":
116+
blocks[:] = [block.copy() for block in initial_blocks]
117+
elif key == " ":
118+
blocks.append(
119+
[block_size // 2 + 10, block_size // 2 + 10, (255, 255, 255, 255)]
120+
)
121+
122+
123+
@canvas.request_draw
124+
def animate():
125+
# Clear
126+
world.fill(0)
127+
world[:, :, 3] = 255
128+
129+
# Draw blocks, in order that they are in the list
130+
for x, y, size, color in blocks:
131+
hs = size // 2
132+
world[y - hs : y + hs, x - hs : x + hs] = color
133+
134+
# Present
135+
context.set_bitmap(world)
136+
137+
138+
loop.run()

0 commit comments

Comments
 (0)