From 588e89bbb85ab280f0fa05bf3d068a3a6afb27d7 Mon Sep 17 00:00:00 2001 From: Taiko2k Date: Tue, 1 Feb 2022 12:11:40 +1300 Subject: [PATCH] add snapshot section --- README.md | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 136 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 67533d1..6eb7ffc 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,11 @@ Wanna make apps for Linux but not sure how to start with GTK? This guide will hopefully help! The intent is to show you how to do common things with basic code examples so that you can get up and running making your own GTK app quickly. +Ultimately you want to be able to refer to the official documentation [here](https://docs.gtk.org/gtk4/) yourself. But it can be hard getting started +without an initial understanding of what you're supposed to do and how you do things. The code examples here should help. + +How to use this tutorial: You can either follow along or just use it to refer to specific examples. + Prerequisite: You have learnt the basics of Python. Ideally have some idea of how classes work. Topics covered: @@ -16,10 +21,13 @@ Topics covered: - Custom drawing with Cairo - Handling mouse input - Setting the cursor + - Setting dark colour theme + - Custom drawing with Snapshot -For beginners, I suggest walking through each example and try to understand what each line is doing. +For beginners, I suggest walking through each example and try to understand what each line is doing. I also recommend taking a look at the docs for each widget. + +Helpful is the [GTK4 Widget Gallery](https://docs.gtk.org/gtk4/visual_index.html) which shows you all the common widgets. -Note that some code examples in this guide require completing previous topics to work. ## A most basic program @@ -397,7 +405,10 @@ from gi.repository import Gtk, Adw, Gio ## Custom drawing area using Cairo -Here we use the [***DrawingArea***](https://docs.gtk.org/gtk4/class.DrawingArea.html) widget. +There are two main methods of custom drawing in GTK4, the Cairo way and the Snapshot way. Cairo provides a more high level +drawing API but uses slow software rendering. Snapshot uses a little more low level API but uses much faster hardware accelerated rendering. + +To draw with Cairo we use the [***DrawingArea***](https://docs.gtk.org/gtk4/class.DrawingArea.html) widget. ```python @@ -535,7 +546,7 @@ Now setting the cursor is easy. You can find a list of common cursor names [here](https://docs.gtk.org/gdk4/ctor.Cursor.new_from_name.html). -## Setting a dark color scheme +# Setting a dark color scheme We can use: @@ -547,9 +558,128 @@ We can use: See [here](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.0.0/styles-and-appearance.html) for more details. -## Further reading +# Custom drawing with Snapshot - - [GTK4 Widget Gallery](https://docs.gtk.org/gtk4/visual_index.html) +As mentioned in the Cairo section, Snapshot uses fast hardware accelerated drawing, but it's a little more complicated to +use. Treat this section as more of a general guide of how it works than a tutorial of how you should do things. + +First, we create our own custom widget class which will implement the [***Snapshot***](https://docs.gtk.org/gtk4/class.Snapshot.html) virtual method. +(To implement a virtual method we need to prepend `do_` to the name as it is in the docs.) + +```python + +class CustomDraw(Gtk.Widget): + def __init__(self): + super().__init__() + + def do_snapshot(self, s): + pass +``` + +Then it can be added in the same way as any other widget. If we want to manually trigger a redraw we can use +the same `.queue_draw()` method call on it. + +If we want the widget to have a dynamic size we can set the usual `.set_hexpand(True)`/`.set_vexpand(True)`, but if it +is to have a fixed size, you would need to implement the [**Measure**](https://docs.gtk.org/gtk4/vfunc.Widget.measure.html) virtual method. + +Have a read of the [***snapshot***](https://docs.gtk.org/gtk4/class.Snapshot.html) docs. It's a little more complex, but once you know what you're doing you +could easily create your own helper functions. You can use your imagination! + +Here's some examples: + +### Draw a solid rectangle + +Here we use: + - [**RGBA Struct**](https://docs.gtk.org/gdk4/struct.RGBA.html) + - [**Rect**](http://ebassi.github.io/graphene/docs/graphene-Rectangle.html) + +```python + def do_snapshot(self, s): + colour = Gdk.RGBA() + colour.parse("#e80e0e") + + rect = Graphene.Rect() # Add Graphene to your imports. i.e. from gi import Graphene + rect.__init__(10, 10, 40, 60) # x, y, w, h. # todo/help this seems like a hacky way of initiating, + # is there a better way? + s.append_color(colour, rect) +``` + +### Draw a solid rounded rectangle / circle + +This is a little more complicated... + + - [***RoundedRect***](https://docs.gtk.org/gsk4/struct.RoundedRect.html) + +```python + colour = Gdk.RGBA() + colour.parse("rgb(159, 222, 42)") # another way of parsing + + rect = Graphene.Rect() + rect.__init__(50, 70, 40, 40) + + rounded_rect = Gsk.RoundedRect() # Add Gsk to your imports. i.e. from gi import Gsk + rounded_rect.init_from_rect(rect, radius=20) # A radius of 360 would make a circle + + s.push_rounded_clip(rounded_rect) + s.append_color(colour, rect) + s.pop() # remove the clip +``` + +### Outline of rect / rounded rect / circle + +Fairly straightforward, see [append_border](https://docs.gtk.org/gtk4/method.Snapshot.append_border.html). + +### An Image + + - See [***Texture***](https://docs.gtk.org/gdk4/class.Texture.html). + +```python + texture = Gdk.Texture.new_from_filename("example.png") + # Warning: For the purposes of demonstration ive shown this declared in our drawing function, but of course + # you would REALLY need to define this somewhere else so that its only called once as we don't want to + # reload/upload the data every draw call. + + # Tip: There are other functions to load image data from in memory pixel data + + rect = Graphene.Rect() + rect.__init__(50, 50, texture.get_width(), texture.get_height()) # Warning: On a HiDPI display the logical and + s.append_texture(texture, rect) # physical measurements may differ in scale, + # typically, by a factor of 2. In most places + # we're dealing in logical units, but these + # methods give physical units. +``` + +### Text + +Text is drawn using Pango layouts. Pango is quite powerful and really needs a whole tutorial on its own, but here's +a basic example of a single line of text: + +```python + colour = Gdk.RGBA() + colour.red = 0.0 # Another way of setting colour + colour.green = 0.0 + colour.blue = 0.0 + colour.alpha = 1.0 + + font = Pango.FontDescription.new() + font.set_family("Sans") + font.set_size(12 * Pango.SCALE) + + context = self.get_pango_context() + layout = Pango.Layout(context) # Add Pango to your imports. i.e. from gi import Pango + layout.set_font_description(font) + layout.set_text("Example text") + + point = Graphene.Point() + point.x = 50 # starting X co-ordinate + point.y = 50 # starting Y co-ordinate + + s.save() + s.translate(point) + s.append_layout(layout, colour) + s.restore() + +``` ## Todo...