Last active
July 17, 2024 17:22
-
-
Save ritobanrc/cf61e574ffb89eae2b3837d8dc2328c6 to your computer and use it in GitHub Desktop.
A minimal example for using Cairo in a GTK `DrawingArea` in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
A minimal example for using Cairo with GTK | |
""" | |
import cairo | |
import gi | |
gi.require_version('Gtk', '3.0') | |
from gi.repository import Gtk, Gdk, GLib | |
from math import pi | |
width = 256 | |
height = 256 | |
angle = 45 | |
def draw(ctx: cairo.Context, width, height): | |
""" | |
This is the draw function, that will be called every time `queue_draw` is | |
called on the drawing area. Currently, this is setup to be every frame, 60 | |
times per second, but you can change that by changing line 95. | |
Ported from the first example here, with minimal changes: | |
https://www.cairographics.org/samples/ | |
""" | |
global angle | |
angle += 1 | |
xc = 128 | |
yc = 128 | |
radius = 100 | |
angle1 = angle * (pi/180) | |
angle2 = 180 * (pi/180) | |
ctx.set_line_width(10.0) | |
ctx.arc(xc, yc, radius, angle1, angle2) | |
ctx.stroke() | |
# draw helping lines | |
ctx.set_source_rgba (1, 0.2, 0.2, 0.6) | |
ctx.set_line_width (6.0) | |
ctx.arc(xc, yc, 10.0, 0, 2*pi) | |
ctx.fill() | |
ctx.arc(xc, yc, radius, angle1, angle1) | |
ctx.line_to(xc, yc) | |
# Adding this fixes a subtle bug where when the two hands would overlap. | |
# This just makes them two separate strokes. | |
ctx.stroke() | |
ctx.arc(xc, yc, radius, angle2, angle2) | |
ctx.line_to(xc, yc) | |
ctx.stroke() | |
def on_draw(da: Gtk.DrawingArea, ctx: cairo.Context): | |
""" | |
A callback called every time `drawingarea.queue_draw` is called. | |
""" | |
alloc = da.get_allocation() | |
width = alloc.width | |
height = alloc.height | |
draw(ctx, width, height) | |
def on_mouse_pressed(da, event, *data): | |
""" | |
This is called when the mouse is pressed | |
""" | |
print("The mouse was pressed!") | |
def main(): | |
""" | |
The main function | |
""" | |
# Create a window, set it up to quit on close | |
win = Gtk.Window() | |
win.connect('destroy', Gtk.main_quit) | |
win.set_default_size(width, height) | |
# Create a DrawingArea, add it to the window, and connect it to the `on_draw` function | |
drawingarea = Gtk.DrawingArea() | |
win.add(drawingarea) | |
drawingarea.connect('draw', on_draw) | |
# Add a button pressed event, and connect it to the `on_mouse_pressed` callback | |
drawingarea.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) | |
drawingarea.connect('button-press-event', on_mouse_pressed) | |
# Tell the drawing area to render | |
drawingarea.queue_draw() | |
def refresh_screen(): | |
drawingarea.queue_draw() | |
GLib.timeout_add(1000 / 60, refresh_screen) | |
# Normally, GUI Libraries don't automatically redraw the screen every | |
# frame. In order to do that, I've setup a timer to call `refresh_screen` | |
# in 16.666 milliseconds (60 FPS). `refresh_screen`, queues a draw command | |
# to re-draw the drawing area and then re-adds the timer for the next | |
# frame. This might seem like a hack, but its more-or-less the "correct" | |
# way to implement this. | |
# | |
# If this is not the behavior you want (perhaps you only want your fractal | |
# to re-draw when the mouse button is pressed), remove this line and the | |
# `refresh_screen` function, and call `drawingarea.queue_draw()`.from | |
# somewhere else | |
GLib.timeout_add(1000 / 60, refresh_screen) | |
# Show the window | |
win.show_all() | |
Gtk.main() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment