Write custom widget with GTK3

10,093

Python3 Gtk3 it is, then:

from gi.repository import Gtk

class SuperSimpleWidget(Gtk.Label):
    __gtype_name__ = 'SuperSimpleWidget'

Here is a non-trivial example that actually does something, namely paints its background and draws a diagonal line through it. I'm inheriting from Gtk.Misc instead of Gtk.Widget to save some boilerplate (see below):

class SimpleWidget(Gtk.Misc):
    __gtype_name__ = 'SimpleWidget'

    def __init__(self, *args, **kwds):
        super().__init__(*args, **kwds)
        self.set_size_request(40, 40)

    def do_draw(self, cr):
        # paint background
        bg_color = self.get_style_context().get_background_color(Gtk.StateFlags.NORMAL)
        cr.set_source_rgba(*list(bg_color))
        cr.paint()
        # draw a diagonal line
        allocation = self.get_allocation()
        fg_color = self.get_style_context().get_color(Gtk.StateFlags.NORMAL)
        cr.set_source_rgba(*list(fg_color));
        cr.set_line_width(2)
        cr.move_to(0, 0)   # top left of the widget
        cr.line_to(allocation.width, allocation.height)
        cr.stroke()

Finally, if you really want to derive from Gtk.Widget then you also have to set up a drawing background. Gtk.Misc does that for you, but Gtk.Widget could be a container that doesn't actually draw anything itself. But inquiring minds want to know, so you could do it like so:

from gi.repository import Gdk

class ManualWidget(Gtk.Widget):
    __gtype_name__ = 'ManualWidget'

    def __init__(self, *args, **kwds):
        # same as above

    def do_draw(self, cr):
        # same as above

    def do_realize(self):
        allocation = self.get_allocation()
        attr = Gdk.WindowAttr()
        attr.window_type = Gdk.WindowType.CHILD
        attr.x = allocation.x
        attr.y = allocation.y
        attr.width = allocation.width
        attr.height = allocation.height
        attr.visual = self.get_visual()
        attr.event_mask = self.get_events() | Gdk.EventMask.EXPOSURE_MASK
        WAT = Gdk.WindowAttributesType
        mask = WAT.X | WAT.Y | WAT.VISUAL
        window = Gdk.Window(self.get_parent_window(), attr, mask);
        self.set_window(window)
        self.register_window(window)
        self.set_realized(True)
        window.set_background_pattern(None)

Edit and to actually use it:

w = Gtk.Window()
w.add(SimpleWidget())
w.show_all()
w.present()
import signal    # enable Ctrl-C since there is no menu to quit
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()

Or, more fun, use it directly from the ipython3 REPL:

from IPython.lib.inputhook import enable_gtk3
enable_gtk3()
w = Gtk.Window()
w.add(SimpleWidget())
w.show_all()
w.present()
Share:
10,093
knocte
Author by

knocte

I'm a project maintainer leading a team of F# and Rust developers, and looking to expand this team (and hire Linux DevOps specialists and/or build engineers). Get in touch if you want to know more. If you're interested in my technical background, read below: In the low-level front, I've used Assembly, C, C++, and starting to learn Rust (could maybe give a go to D too). In the too-high-level front, I've used PHP, Python, JavaScript, Ruby, but not willing to use them again if I can avoid it. These days all my new code is written in F# (or TypeScript if I need to use some JavaScript library), although I still need to touch a lot of C# code. When I was freelancing, I was willing to learn Elm in case I needed to do some web+frontend development and they didn't let me write TypeScript in strict mode (plus tslint), or Gleam in case I had to interoperate with any BEAM-based language, or Ceylon/Frege (along with ParparVM if iOS is needed) or maybe Swift, in case I needed to implement mobile apps and they didn't let me use Xamarin.Forms+F#. In regards to databases, I'm an old-school guy who thinks normalized data usually beats any NoSQL/NewSQL solution, except for rare BigData indexing/denormalization scenarios.

Updated on June 04, 2022

Comments

  • knocte
    knocte almost 2 years

    I am trying to find the simplest example of a custom widget being written for Gtk-3.

    So far the best thing I've found is this (using PyGTK), but it seems to be targeted to Gtk-2.

    BTW: I don't care the language it is written in, but if we can avoid C++, much better!

  • knocte
    knocte over 10 years
    this is from your blog right? when you say "This is rather different from the way things worked in GTK 2, and there are probably plenty of GTK 2 instructions still floating around on the Internet." I recommend you to explicitly say that it's a GTK 3 technique, to make it more explicit.
  • knocte
    knocte over 10 years
    and not sure I'm going to mark this answer as SOLVED because it doesn't really provide a sample, but thanks for the link
  • ptomato
    ptomato over 10 years
    Right, I don't think it answers your question satisfactorily either. :-(
  • erick2red
    erick2red over 10 years
    For starters, I don't think there's a simple answer for that question. No one use python to write simple widgets. There's the code inside gtk+ itself. GtkButton is a supersimple widget to start with. You can read it.
  • ptomato
    ptomato over 10 years
    I would use Python to write simple widgets, I just have never gotten around to writing a tutorial about it! ;-)