|
| 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