在Python GTK3中设置条目背景颜色并恢复默认的最佳方式是什么?

6
什么是设置一个条目的背景色并将其恢复为默认颜色的最佳方法?
我的脚本现在可以工作,但我非常确定这不是最好的方法。另外,我仍然有两个问题:
1. 如果我插入一个不包含字符串“red”或“green”的文本并选择此文本,则无法看到我的选择,因为它全部是白色的。 2. 我认为有比我将self.entry_default_background_color_str插入CSS文本更好的方法。
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()

1
关于问题中的代码,有一个小提示:它使用了 self.name_entry 而不是监听器传递的小部件。虽然这对于只有一个 Entry 可能有效,但对于多个 Entry 则不行,因此请使用由监听器传递的那个小部件,否则可能会将样式应用到错误的 Entry 上。 - B8vrede
1个回答

5
我首先会解决你提到的问题,因为这些问题揭示了GTK和OP代码中发生的事情。答案和正确的代码都在回答的最底部。
  1. 如果我插入一个文本,不包含字符串“red”或“green”,并选择此文本,我看不到我的选择,因为它全是白色。

发生这种情况的原因是使用了background属性,这将Entry的所有背景相关属性设置为该颜色。因此,选择和“真正”的背景都是相同的颜色。

那么问题是我们应该使用哪个属性,这是GTK的一部分,文档很差,但是我们可以使用GtkInspector来查看哪些样式属性正在更改。这告诉我们应该使用background-image,而background-color用于选择的背景。

仅将background-image设置为颜色是无效的,因为需要图像。因此,我们现在必须找出一种使我们的color成为可设置为background-image的内容的方法。幸运的是,检查器向我们展示了GTK的内部方式,即通过像这样包装颜色:linear-gradient(red)。这样做会创建一个均匀的红色图像,可用作背景。

将此知识应用于您的代码,我们可以得到:

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. 我认为还有更好的方法可以将self.entry_default_background_color_str插入CSS文本。

的确有更好的方法,即不要这样做。我们只需向CssProvider提供一个空版本的CSS即可轻松返回默认值,这将覆盖旧的CSS,从而删除任何旧的样式属性,例如颜色。

将此与前面的部分结合起来,我们得到:

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 {}')

什么是设置一个条目的背景颜色并将其恢复为默认颜色的最佳方法?
现在我已经解决了您代码中的问题,现在是这个非常重要的问题。您现在所做的方式是替换CSS文件,虽然可以正常工作,但从长远来看效率非常低下。通常,您会加载CSS并使用类和ID告诉它应用哪些样式。
下面我改编了您的代码以此方式进行操作,请查看注释以了解说明。
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")

1
非常感谢!我对这个答案非常满意 :-) - oxidworks
最新的GTK3建议使用background-image: image(red);。使用linear-gradient()中的一种颜色停止已经被弃用。 - haolee
好的,一旦我有机会测试它,就会更新代码。 - B8vrede

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接