5

My latest head-scratcher is to build a silly little app in Python3 using GTK3, with colours other than fog-grey on the buttons. I have spent the last few days googling for how to do this, and so far everything I have tried has failed. Not just failed, but failed silently, with no error messages to give me any clue as to what is going on.

This is my test app:

from gi.repository import Gtk, Gdk

class ButtonWindow(Gtk.Window):

    def __init__(self):
        super().__init__(title="Button Test")
        self.set_border_width(10)
        hbox = Gtk.Box(spacing=10)
        self.add(hbox)
        hbox.set_homogeneous(False)

        # make the button
        button = Gtk.Button('Test Button')
        hbox.pack_start(button, True, True, 0)

        # try to change its colour ....

#        button.modify_base(Gtk.StateType.NORMAL, Gdk.color_parse('green'))
#        button.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(0, 1, 0, 1))
#        button.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(0x00ff00))
#        button.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse("green"))
#        button.modify_bg(Gtk.StateType.ACTIVE, Gdk.color_parse("green"))
#        button.modify_bg(Gtk.StateType.SELECTED, Gdk.color_parse("green"))

        # attempt to change the style ....

#        style = button.get_style().copy()
#        style.bg[Gtk.StateType.NORMAL] = Gdk.color_parse('green')
#        style.bg[Gtk.StateType.ACTIVE] = Gdk.color_parse('red')
#        style.bg[Gtk.StateType.SELECTED] = Gdk.color_parse('blue')
#        style.bg[Gtk.StateType.PRELIGHT] = Gdk.color_parse('black')
#        button.set_style(style)

        # ok, let's try changing the box ....

#        hbox.modify_base(Gtk.StateType.NORMAL, Gdk.color_parse('green'))
#        hbox.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(0,1,0,1))
#        hbox.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(0x00ff00ff))
#        hbox.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse('green'))

window = ButtonWindow()        
window.connect("delete-event", Gtk.main_quit)
window.show_all()
Gtk.main()

I have left my failed attempts in as comments. As noted above, as far as the application is concerned, it appears to have worked, because none of the above variations generate any error messages. However, none of them seem to work for me, because the buttons remain the colour of stale dishwater.

FYI I am using Python 3.2.3 under Ubuntu 12.04 with python3-gi and python3-gi-cairo installed from the standard repository.

Can someone please point me in the right direction?

EDIT: The following is a re-worked example based on @mike's answer. This works, but there are some issues with it, possibly to be addressed in some follow-up questions. The issues are:

  1. Why does background have to used on Ubuntu instead of background-color, and then only for the button?
  2. I still have some problems getting font styling to work, but at least now I have a working example to play with.
  3. Can different styles/colours be applied to different buttons, e.g. on the basis of the text or some other attribute?

So, the code:-

from gi.repository import Gtk, Gdk

class ButtonWindow(Gtk.Window):

    def __init__(self):
        super().__init__(title="Button Test")
        self.set_border_width(10)

        hbox = Gtk.Box(spacing=10)
        self.add(hbox)
        hbox.set_homogeneous(False)

        # make the button
        button = Gtk.Button('Test Button')
        hbox.pack_start(button, True, True, 0)

# get the style from the css file and apply it
cssProvider = Gtk.CssProvider()
cssProvider.load_from_path('gtkStyledButtonTest.css')
screen = Gdk.Screen.get_default()
styleContext = Gtk.StyleContext()
styleContext.add_provider_for_screen(screen, cssProvider,
                                     Gtk.STYLE_PROVIDER_PRIORITY_USER)

window = ButtonWindow()        
window.connect("delete-event", Gtk.main_quit)
window.show_all()
Gtk.main()

and the css file looks like this:-

GtkWindow {
    background-color: #0000ff;
}

GtkButton {
    color: #ff0000;
    background: #00ff00;
}

I hope someone finds this useful.

Bobble
  • 2,791
  • 4
  • 23
  • 30
  • You need to wrap it with an eventbox... http://stackoverflow.com/questions/7127093/pygtk-change-background-color-of-gtkhbox-widget – boosth Aug 19 '12 at 12:17
  • Thanks for the hint, @boosth. I had actually given up on GTK and started to implement my app with tkinter/ttk, but I might give GTK another go, now! – Bobble Aug 20 '12 at 13:21

3 Answers3

6

Even though this is a old question, I'd like to add an answer referring to question 3 just for the reference.

GTK3 adds the concept of style classes. So to get different colored buttons you can address them directly be name or add a style class to its context. All this is explained in the links mike provided in his answer.

Here is a simple example how to use style classes to highlight invalid text in entries:

from gi.repository import Gtk, Gdk

class MainWindow(Gtk.Window):

    def __init__(self):
        super().__init__()
        vbox = Gtk.Box(spacing=10,orientation=Gtk.Orientation.VERTICAL)
        self.add(vbox)

        self.entries = [ Gtk.Entry() for i in range(3) ]
        for e in self.entries:
            vbox.pack_start(e, True, True, 0)
            e.connect("changed", self.on_entry_changed)
            e.set_text('123')

        button=Gtk.Button('ok',name='ok-button')
        vbox.pack_end(button,True,True,0)


    def on_entry_changed(self,entry):
        ctx = entry.get_style_context()
        if not entry.get_text().isnumeric():
            ctx.add_class('invalid')
        else:
            ctx.remove_class('invalid')


cssProvider = Gtk.CssProvider()
cssProvider.load_from_path('style.css')
screen = Gdk.Screen.get_default()
styleContext = Gtk.StyleContext()
styleContext.add_provider_for_screen(screen, cssProvider,
                                     Gtk.STYLE_PROVIDER_PRIORITY_USER)

window = MainWindow()
window.connect("delete-event", Gtk.main_quit)
window.show_all()
Gtk.main()

with style.css:

GtkEntry.invalid {
    background-color: #ffaaaa;
    background: #ffaaaa;
}

GtkButton#ok-button {
    background-color: green;
    background: green;
}
buergi
  • 6,039
  • 3
  • 19
  • 15
  • Another relevant link is https://wiki.gnome.org/HowDoI/Buttons which mentions some standard classes for _suggested_ and _destructive_ buttons, which don't need custom CSS and get highlighted in blue and red respectively using the default Adwaita theme. Green is a nice one, so thanks for indicating it's as simple as adding `background` rules in CSS... Now all I have to do is figure out how to load a CSS file under GTKmm, unless there's a way to add CSS rules in the code? I'd rather not rely on an external file if I can avoid it. – underscore_d Jun 13 '16 at 11:18
5

The preferred way in GTK3 is to use CSS for styling. Under Ubuntu 12.04 you may need to use background instead of background-color. But I don't know Python so I'll just give a link.

https://thegnomejournal.wordpress.com/2011/03/15/styling-gtk-with-css/

mike
  • 1,135
  • 4
  • 22
  • 39
  • Many Thanks Mike! I checked over your links and this looks very much like what I have been looking for. I still don't understand CSS that much, but I can see (what was not mentioned in any of the GTK tutorials I have looked at) that an understanding of CSS is necessary for doing anything more than very basic stuff with GTK. – Bobble Aug 29 '12 at 02:37
  • I have added a working example to my original question based on this answer. – Bobble Aug 29 '12 at 10:20
  • When you have multiple widgets of the same type, to style them individually you'll need to name them using gtk_widget_set_name() or the equivalent in Python. Then in the CSS use their name preceded by the the pound symbol which is called the ID selector in CSS. http://www.w3.org/TR/css3-selectors/#id-selectors My examples are with C not python but you might find them useful. http://www.gtkforums.com/viewtopic.php?f=3&t=988&p=72088=Starting+with+version+3#p72088 – mike Aug 29 '12 at 19:18
  • Thanks again! It's funny how coding in Python is brushing up my C skills :) However, I guess that is the great bonus of Gtk introspection, it matters a lot less what primary language you are working in. – Bobble Aug 31 '12 at 14:50
  • none of these links in the answer actually point to anything useful anymore :( – fossfreedom Apr 29 '15 at 15:47
0

Inspired by @boosth, this is the modified code (wrap the button, and apply the colour to the wrapper - see lines commented with # <----).

However, while it changes the colour of the event box, the button itself remains the same. So, this is NOT what I was looking for, but so far this is the best answer.

from gi.repository import Gtk, Gdk

class ButtonWindow(Gtk.Window):

    def __init__(self):
        super().__init__(title="Button Test")
        self.set_border_width(10)

        hbox = Gtk.Box(spacing=10)
        self.add(hbox)
        hbox.set_homogeneous(False)

        # make the button
        button = Gtk.Button('Test Button')
        buttonWrapper = Gtk.EventBox()                  # <----
        buttonWrapper.add(button)                       # <----
        hbox.pack_start(buttonWrapper, True, True, 0)   # <----

        # change the colour of the wrapper ....
        buttonWrapper.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse("green"))
        buttonWrapper.modify_bg(Gtk.StateType.ACTIVE, Gdk.color_parse("red"))
        buttonWrapper.modify_bg(Gtk.StateType.SELECTED, Gdk.color_parse("blue"))


window = ButtonWindow()        
window.connect("delete-event", Gtk.main_quit)
window.show_all()
Gtk.main()

There must be a way to do this....

Bobble
  • 2,791
  • 4
  • 23
  • 30