poktuba
poktuba

Reputation: 13

Update/refresh Widget in Gtk3 (pygobject) and python3

I want the colour of a button to change when I click on it.
Or more general I want to update/refresh the Gtk.Button or Gtk.Box. The entire Gtk.Window would be fine as well. The only way I found to update a widget in Gtk3 is the Gtk.widget.queue_draw() method, but it looks like it only works with a Gtk.DrawingArea (Drawin with cairo on Zetcode, it's for Gtk2 but I've read there haven't been made many changes in this section). I would assume it works on any widget since it is named gtk.widget.queue_draw and not gtk.drawingArea.queue_draw and also it's not producing an error but it appears to be omitted.
I might have a wrong understanding of how it is supposed to work though. The only guis I've made before are some time ago and were simple things in Java.
So here is my code example:

# -*- coding: utf-8 -*-

from gi.repository import Gtk, Gdk
import cairo

a = 0
class ProgWindow(Gtk.Window):
    def __init__(self):
        super(ProgWindow, self).__init__() # don't know what this is doing, works also without
        self.window = Gtk.Window.__init__(self, title="example")
        self.connect("delete-event", Gtk.main_quit)
        box = Gtk.Box()
        self.add(box)

        button = Gtk.Button("Hello")
        if (a==0):
            button.set_name('green_button')
            button.set_tooltip_text("should be green")
        else:
            button.set_name('red_button')
            button.set_tooltip_text("should be red")
        button.connect("clicked", callback_clicked)
        box.add(button)

def callback_clicked(self):
    global a
    print(a)
    a = 1
    self.queue_draw()
    gui()                 # creates a new window with the correct colour and tool tip

def gui():
    win = ProgWindow()
    win.show_all()
    Gtk.main()


style_provider = Gtk.CssProvider()

css_data = """
#green_button{
    background: #009900;
    font-size: 120%;
    font-weight: 600;
}

#red_button{
    background: #FF0000;
    font-size: 120%;
    font-weight: 600;
}
"""

style_provider.load_from_data(css_data.encode())
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(),
style_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)

if __name__ == "__main__":
    gui()

I've also read somewhere, that it is possible to invalidate an area. Would that maybe be the way to do it? Also I've read that things get updated once the Gtk.main() loop is reached again. If so, how do I evoke this? As I said I'm quit new to gui programming but my understanding is, that this is where it's "stuck" most of the time while waiting for i.e. user input.
Or would removing the widget and place a new one in the same space do the job?
Any help and explanation would be greatly appreciated
Thanks

Upvotes: 1

Views: 2938

Answers (2)

gianmt
gianmt

Reputation: 1182

You have made it really really too complicated, here below a simplified version that will make the button green when clicked.

p.s. changing the colors of the widgets like this should realy be an exception and not the rule, breaking user themes is generally not a good idea.

from gi.repository import Gtk

class CSSButtonApp(object):
    def __init__(self):
        self.mycolor = '''
        GtkButton {
            border-image: none;
            background-image: none;
            background-color: green;
            color: white;
        }
        '''
        self.window = Gtk.Window()
        self.window.set_title('CSS Button')
        self.window.set_default_size(200, 100)
        self.window.set_border_width(10)
        self.window.connect('destroy', lambda w: Gtk.main_quit())

        button = Gtk.Button(label="My Button")
        self.window.add(button)

        provider = Gtk.CssProvider()
        provider.load_from_data(self.mycolor)

        button.connect("clicked", self.apply_css, provider)

        self.window.show_all()

    def apply_css(self, widget, provider):
        Gtk.StyleContext.add_provider(widget.get_style_context(),
                                      provider,
                                      Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

    def main(self):
        Gtk.main()

if __name__ == '__main__':
    app = CSSButtonApp()
    app.main()

Upvotes: 1

ptomato
ptomato

Reputation: 57920

You don't need to run queue_draw() for this. You also shouldn't recreate your GUI every time you want the button to change color. This is likely messing things up, because in gui() you create a new window and start a new main loop with Gtk.main(), leading to many main loops running recursively. This is something you don't want.

Instead, use button.set_name('green-button') or red-button to change the color. No further action should be necessary other than returning from the signal handler.

Even better would be to use style classes instead of IDs — you might want to have more than one green or red button. In that case, put .green-button and .red-button in your CSS (instead of #green-button and #red-button) and use this to change the color:

button.get_style_context().remove_class('green-button')
button.get_style_context().add_class('red-button')

Or vice versa.

Upvotes: 3

Related Questions