Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to set Entry Background Color in Python GTK3 and set back to default

Tags:

python

css

gtk3

What is the best way to set background color for one entry and set it back to the default color?

My script is now working but I am very sure this is not the best way.
Also I still have two problems:

  1. If I insert a text, not containing string "red" or "green" and select this text I cant see my selection because It is all white.
  2. I think there are better ways then the way I insert self.entry_default_background_color_str into the CSS text.

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

class Window(Gtk.Window):

    def __init__(self):

        self.screen = Gdk.Screen.get_default()
        self.gtk_provider = Gtk.CssProvider()
        self.gtk_context = Gtk.StyleContext()
        self.gtk_context.add_provider_for_screen(self.screen, self.gtk_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)


        Gtk.Window.__init__(self, title="Check Input")
        self.set_size_request(300, 80)
        self.mainbox = Gtk.VBox()
        self.add(self.mainbox)

        # entry
        self.name_entry      = Gtk.Entry()
        self.name_entry.set_name("name_entry")
        self.mainbox.pack_start(self.name_entry, True, True, 0)
        self.name_entry.connect("changed", self.check_input)

        entry_context = self.name_entry.get_style_context()
        self.entry_default_background_color = entry_context.get_background_color(Gtk.StateType.NORMAL)
        self.entry_default_background_color_str = self.entry_default_background_color.to_string()

        self.show_all()


    def check_input(self, _widget=None):        
            if "red" in self.name_entry.get_text():
                self.gtk_provider.load_from_data('#name_entry { background: red; }')
            elif "green" in self.name_entry.get_text():
                self.gtk_provider.load_from_data('#name_entry { background: green; }')
            else:
                self.gtk_provider.load_from_data('#name_entry { background: ' + self.entry_default_background_color_str + '; }')


def main():
    window = Window()
    Gtk.main()


if __name__ == "__main__":
    main()
like image 538
oxidworks Avatar asked Aug 21 '16 14:08

oxidworks


People also ask

How do you change the background color in python?

Right-click the upper-left corner of the Python console window and select Properties. In the dialog box that appears, pick the tab labeled Colors. On it you can set the screen background and text color.


1 Answers

I will first address the issues you mention, as they give insight into what is going on in GTK and OP's code. The answer to the main question (and proper code for doing this) is all the way at the bottom of the answer.


  1. If I insert a text, not containing string "red" or "green" and select this text I cant see my selection because It is all white.

The reason this happens is because the background property is used, this sets all background related properties of the Entry to that color. So both that of the selection as well as the "real" background.

Then the question is what property do we use, this is a part of GTK which is poorly documented but we can find out using the GtkInspector which lets us see what style properties are changing. This yields we should use background-image instead and that background-color is used for the background of the selection.

Just setting the background-image to the color doesn't work, that gives a error because a image is expected. So now we have to figure out a way to make our color into something we can set as a background-image luckily the inspector shows us the way GTK does it internally namely by wrapping the color like this: linear-gradient(red). Doing so creates a uniform red image which can be used as the background.

Applying this knowledge to your code gives us:

if "red" in self.name_entry.get_text():
    self.gtk_provider.load_from_data('#name_entry { background-image: linear-gradient(red); }')
elif "green" in self.name_entry.get_text():
    self.gtk_provider.load_from_data('#name_entry { background-image: linear-gradient(green); }')

  1. I think there are better ways then the way I insert self.entry_default_background_color_str into the CSS text.

There is indeed a better way, namely don't do it. We can easily return to the default by just feeding the CssProvider an empty version of the css, this will overwrite the old one and thus remove any old style properties like for example the color.

Combining this with the previous section gives us:

if "red" in self.name_entry.get_text():
    self.gtk_provider.load_from_data('#name_entry { background-image: linear-gradient(red); }')
elif "green" in self.name_entry.get_text():
    self.gtk_provider.load_from_data('#name_entry { background-image: linear-gradient(green); }')
else:
    self.gtk_provider.load_from_data('#name_entry {}')

What is the best way to set background color for one entry and set it back to the default color?

Now that I addressed the issues with your code, now this all important question. The way your are doing it now is replacing the CSS file, which works fine but in the long run is really inefficient. Normally you would load the CSS and uses classes and ids to tell it which styling to apply.

Below I adapted your code to do it this way, check the comments for the explanation.

def __init__(self):

    screen = Gdk.Screen.get_default()
    gtk_provider = Gtk.CssProvider()
    gtk_context = Gtk.StyleContext()
    gtk_context.add_provider_for_screen(screen, gtk_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

    # Create the window
    Gtk.Window.__init__(self, title="Check Input")
    self.set_size_request(300, 80)
    self.mainbox = Gtk.VBox()
    self.add(self.mainbox)

    # Load the CSS
    gtk_provider.load_from_data("""
    #name_entry.red { background-image: linear-gradient(red); }
    #name_entry.green { background-image: linear-gradient(green); }
    """)

    # Create the entry and give it a name which will be the ID
    name_entry = Gtk.Entry()
    name_entry.set_name("name_entry")
    self.mainbox.pack_start(name_entry, True, True, 0)

    # Add the listener
    name_entry.connect("changed", self.check_input)

    self.show_all()


def check_input(self, entry):
    # Get the style context for this widget
    entry_style_context = entry.get_style_context()

    # Check if our text contains red
    if "red" in entry.get_text():
        # Add the red class, so now the styling with .red is applied
        entry_style_context.add_class("red")

    # Check if our text contains green
    elif "green" in entry.get_text():
        # Add the red class, so now the styling with .green is applied
        entry_style_context.add_class("green")
    else:
        # When the text doesn't contain it remove the color classes to show the default behaviour
        entry_style_context.remove_class("red")
        entry_style_context.remove_class("green")
like image 63
B8vrede Avatar answered Oct 21 '22 11:10

B8vrede