2

I've been unable to figure out what I'm doing wrong. I'm sure it's something simple but escaping me. Even in the following example I'm unable to get this working. I cannot seem to apply a CSS style to a GtkEntry widget in a single GtkWindow. I'm simply trying to turn the background of the entry box red.

Everything I've reviewed and examples I've seen all look like the below and nobody else seems to be stumped.

Here is my Python:

import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk

builder = Gtk.Builder()
builder.add_from_file("entry.glade")

window = builder.get_object('window1')
window.connect('destroy', Gtk.main_quit)

css = "#red { background-image: linear-gradient(red); }"
provider = Gtk.CssProvider()
provider.load_from_data(css)
Gtk.StyleContext().add_provider_for_screen(Gdk.Screen.get_default(), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

entry = builder.get_object('entry1')
entry_style_context = entry.get_style_context()
entry_style_context.add_class("red")

window.show_all()

Gtk.main()

And here is the Glade XML:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk+" version="3.0"/>
  <!-- interface-naming-policy project-wide -->
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkVBox" id="vbox1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkEntry" id="entry1">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="invisible_char">●</property>
            <property name="primary_icon_activatable">False</property>
            <property name="secondary_icon_activatable">False</property>
            <property name="primary_icon_sensitive">True</property>
            <property name="secondary_icon_sensitive">True</property>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

If I set the CSS to .entry { background-image: linear-gradient(red); } it works, but then all GtkEntry boxes would be red (which I don't want).

What in the world am I missing?

4
  • Relevant Import CSS file in Python GTK3 program created using Glade, watch the note " if there is no background-image set" Commented Nov 25, 2019 at 23:15
  • Also of note is the line <requires lib="gtk+" version="2.24"/> You should not be mixing Gtk3 with Gtk2. Bad things are bound to happen. It has nothing to do with your problem, but bad things will come your way. mwahahahaha! Commented Nov 26, 2019 at 7:12
  • Thanks @PaulChilds, changed the glade to 3.0 for consistency (no change though). Commented Nov 26, 2019 at 16:30
  • @stovfl - Doesn't seem to be the cause here as I'm trying to set the background, not the color and it DOES work a the .entry level, but I DID add a require_version for Gdk as noted in the post, still no joy however. Commented Nov 26, 2019 at 16:31

3 Answers 3

4

Question: Apply CSS to a GtkEntry Widget

As ptomato in his answer describes, you can't use css = "#red ... if you don't define a name for your widget.

Define a widgets name, using either:

  • In the glade file: <property name="name">red</property>
  • In the script: entry = builder.get_object('entry1') entry.set_name('red')


Method 1: Using a widgets name, css = b"#entry ...:

  1. Set the widget name in the glade file to entry
        <child>
          <object class="GtkEntry" id="entry1">
            <property name="name">entry</property>
    
  2. Define your CSS using the widgets name entry:

        css = b"#entry {background-image: linear-gradient(red, orange); color: white;}"
    

    Working example, using a widgets name:

    class App:
        def __init__(self):
            super().__init__()
    
            builder = Gtk.Builder()
            builder.add_from_file("entry.glade")
    
            window = builder.get_object('window1')
            window.connect('destroy', Gtk.main_quit)
    
            css = b"#entry {background-image: linear-gradient(red, orange);}"
            provider = Gtk.CssProvider()
            provider.load_from_data(css)
            Gtk.StyleContext()\
                .add_provider_for_screen(Gdk.Screen.get_default(),
                                         provider,
                                         Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
    
            window.show_all()
    
    if __name__ == "__main__":
        main = App()
        Gtk.main()
    

Method 2: Using a widgets style class, css = b".entry.red ...:

  • Gtk.Widget.get_style_context, Gtk.StyleContext.add_class

    1. Define your CSS using nested style class .entry, .red, .blue:
            css = b"""
            .entry {color: white;}
            .entry.red {background-image: linear-gradient(red, orange);}
            .entry.blue {background-image: linear-gradient(blue, lightblue);}
    
    1. Add the named style classes to the widget:
            entry1 = builder.get_object('entry1')
            entry1.get_style_context().add_class("entry")
            entry1.get_style_context().add_class("red")
    
            # the same for `entry2`
            ...
    

    Working example using a widgets style class:

    enter image description here

    class App:
        def __init__(self):
            super().__init__()
    
            builder = Gtk.Builder()
            builder.add_from_file("entry.glade")
    
            window = builder.get_object('window1')
            window.connect('destroy', Gtk.main_quit)
    
            css = b"""
            .entry { color: white;}
            .entry.red { background-image: linear-gradient(red, orange);}
            .entry.blue { background-image: linear-gradient(blue, lightblue);}
            """
            provider = Gtk.CssProvider()
            provider.load_from_data(css)
            Gtk.StyleContext()\
                .add_provider_for_screen(Gdk.Screen.get_default(),
                                         provider,
                                         Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
    
            entry1 = builder.get_object('entry1')
            entry1.set_text('TEST')
            entry1.get_style_context().add_class("entry")
            entry1.get_style_context().add_class("red")
    
            entry2 = builder.get_object('entry2')
            entry2.set_text('TEST')
            entry2.get_style_context().add_class("entry")
            entry2.get_style_context().add_class("blue")
    
            window.show_all()
    
    if __name__ == "__main__":
        main = App()
        Gtk.main()
    

Tested with Python: 3.5 - gi.__version__: 3.22.0 - Glade 3.20.0

Sign up to request clarification or add additional context in comments.

Comments

2

In regular browser CSS, #foo refers to an HTML element with the ID foo (set with, e.g. id="foo") and .foo refers to an element with the style class (set with, e.g. class="foo", or multiple classes, e.g. class="foo bar".)

In GTK CSS the ID translates to the widget name (set with, e.g. entry.set_name('foo'), or <property name="name">foo</property> in the XML file; unfortunately not the same as id="foo" in the XML file) and the class is still style class (but in GTK it's set with, e.g. entry.get_style_context().add_class('foo'). Since your widget has a style class but no name, that's why you have to use the dot syntax.

2 Comments

"translates to the widget name": To clarify, can you edit your answer with a full entry.set_name('entry1') example. Isn't in <object class="GtkEntry" id="entry1">, entry1 the name of the widget?
Hopefully the example of how to set it in the XML file should clarify it.
0

OK, well with a fresh mind and looking at this again, I think I've figured it out. I'm not sure exactly why it's working this way, but hopefully someone smarter than I can chime in.

For whatever reason, I need to specify the widget type and class name in dot notation as hash notation isn't working as one would expect in CSS/HTML land.

This does NOT work: css = "#foo { background-image: linear-gradient(red); }

This DOES work: css = "entry.foo { background-image: linear-gradient(red); }

I've verified this by adding a second entry box to the Glade and doing this:

css = """
.entry.red { background-image: linear-gradient(red); }
.entry.blue { background-image: linear-gradient(blue); }
"""
provider = Gtk.CssProvider()
provider.load_from_data(css)
Gtk.StyleContext().add_provider_for_screen(Gdk.Screen.get_default(), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

builder.get_object('entry1').get_style_context().add_class("red")
builder.get_object('entry2').get_style_context().add_class("blue")

Now I have one red and one blue entry box.

1 Comment

Get the following: provider.load_from_data(css) TypeError: Item 0: Must be number, not str and tk-WARNING : Theme parsing error: <data>:2:58: Using one color stop with linear-gradient() is deprecated.. No, backgroung color.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.