Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Programming question]: Interactive drawing with ipycanvas #121

Open
haesleinhuepf opened this issue Nov 29, 2024 · 5 comments
Open

[Programming question]: Interactive drawing with ipycanvas #121

haesleinhuepf opened this issue Nov 29, 2024 · 5 comments
Assignees

Comments

@haesleinhuepf
Copy link
Owner

I would like to use ipycanvas in a Jupyter Notebook to let the user draw something. Whenever the user releases the mouse, a callback is called. The user can use a freehand tool to draw lines on the canvas and has a panel with 10 buttons for choosing 10 colors for the drawings.

git-bob think about how to do this.

@haesleinhuepf haesleinhuepf self-assigned this Nov 29, 2024
Copy link
Contributor

This message was generated by git-bob (version: 0.14.1dev, model: claude-3-5-sonnet-20241022, log), an experimental AI-based assistant. It can make mistakes and has limitations. Check its messages carefully.

Hi! I can help you set up an interactive drawing application with ipycanvas. Here's a minimal example using ipywidgets for the color buttons and ipycanvas for drawing:

import ipycanvas
import ipywidgets as widgets
from IPython.display import display

# Create canvas
canvas = ipycanvas.Canvas(width=500, height=500)
canvas.stroke_style = "black"
canvas.line_width = 2

# Create color buttons
colors = ['red', 'blue', 'green', 'yellow', 'orange', 
          'purple', 'brown', 'pink', 'gray', 'black']
buttons = [widgets.Button(description='', 
                         layout=widgets.Layout(width='30px', height='30px'),
                         style=dict(button_color=c)) for c in colors]

# Variables for drawing
drawing = False
last_x = 0
last_y = 0

def on_mouse_down(x, y):
    global drawing, last_x, last_y
    drawing = True
    last_x = x
    last_y = y

def on_mouse_move(x, y):
    global drawing, last_x, last_y
    if drawing:
        canvas.begin_path()
        canvas.move_to(last_x, last_y)
        canvas.line_to(x, y)
        canvas.stroke()
        last_x = x
        last_y = y

def on_mouse_up(x, y):
    global drawing
    drawing = False
    # Add your callback here
    print("Mouse released!")

def on_color_click(b):
    canvas.stroke_style = b.style.button_color

# Connect mouse events
canvas.on_mouse_down(on_mouse_down)
canvas.on_mouse_move(on_mouse_move)
canvas.on_mouse_up(on_mouse_up)

# Connect button events
for button in buttons:
    button.on_click(on_color_click)

# Display everything
colors_box = widgets.HBox(buttons)
display(colors_box, canvas)

You can now draw on the canvas and switch colors using the buttons. Add your callback function in the on_mouse_up function.

@haesleinhuepf
Copy link
Owner Author

Ok cool. Make the canvas a bit smaller, add a black frame around it and three panels on the right where I can modify text when the callback is called.

@haesleinhuepf
Copy link
Owner Author

Also I need a PIL Image of thr canvas when the callback is called.

git-bob think about this

Copy link
Contributor

This message was generated by git-bob (version: 0.14.1dev, model: claude-3-5-sonnet-20241022, log), an experimental AI-based assistant. It can make mistakes and has limitations. Check its messages carefully.

Here's the modified code with the requested changes:

import ipycanvas
import ipywidgets as widgets
from IPython.display import display
from PIL import Image
import io

### Create canvas
canvas = ipycanvas.Canvas(width=300, height=300)
canvas.stroke_style = "black"
canvas.line_width = 2

# Add black frame
def draw_frame():
    canvas.stroke_rect(0, 0, 300, 300)
draw_frame()

### Create color buttons and text panels
colors = ['red', 'blue', 'green', 'yellow', 'orange', 
          'purple', 'brown', 'pink', 'gray', 'black']
buttons = [widgets.Button(description='', 
                         layout=widgets.Layout(width='30px', height='30px'),
                         style=dict(button_color=c)) for c in colors]

text_panels = [widgets.Textarea(
    description=f'Panel {i+1}:',
    layout=widgets.Layout(width='200px', height='100px')
) for i in range(3)]

### Variables for drawing
drawing = False
last_x = 0
last_y = 0

def on_mouse_down(x, y):
    global drawing, last_x, last_y
    drawing = True
    last_x = x
    last_y = y

def on_mouse_move(x, y):
    global drawing, last_x, last_y
    if drawing:
        canvas.begin_path()
        canvas.move_to(last_x, last_y)
        canvas.line_to(x, y)
        canvas.stroke()
        last_x = x
        last_y = y

def on_mouse_up(x, y):
    global drawing
    drawing = False
    
    # Get canvas as PIL Image
    image_data = canvas.to_file()
    pil_image = Image.open(io.BytesIO(image_data))
    
    # Update text panels
    text_panels[0].value = f"Mouse released at {x}, {y}"
    text_panels[1].value = f"Image size: {pil_image.size}"
    text_panels[2].value = "Drawing finished"

def on_color_click(b):
    canvas.stroke_style = b.style.button_color
    draw_frame()  # redraw frame after color change

### Connect events
canvas.on_mouse_down(on_mouse_down)
canvas.on_mouse_move(on_mouse_move)
canvas.on_mouse_up(on_mouse_up)

for button in buttons:
    button.on_click(on_color_click)

### Layout
colors_box = widgets.HBox(buttons)
panels_box = widgets.VBox(text_panels)
display(widgets.HBox([
    widgets.VBox([colors_box, canvas]),
    panels_box
]))

The canvas will have a black frame, three text panels on the right, and the callback provides a PIL Image of the canvas content.

@haesleinhuepf
Copy link
Owner Author

For the record: I modified this sample and made the result available: https://github.com/haesleinhuepf/vlm-pictionary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant