How do I update/redraw a GTK Widget (GTKLabel) internally without a key press event using python?

13,056

Solution 1

You'll want to use the Widget's queue_draw function:

The queue_draw_area() method invalidates the rectangular area of the widget (...) by calling the gtk.gdk.Window.invalidate_rect() method on the widget's window and all its child windows.

But, as you are already doing that in your set method, my guess is that the

if hasattr(item,'show')

check prevents the item from updating. Another check is to call the queue_draw function on the entire window, and see if that updates things.

Alternatively, force the events that are queued to be processed as follows:

while gtk.events_pending():
    gtk.main_iteration_do(True)

Solution 2

One thing that happens in the code is that while do_something_that_takes_a_while is executed, no events are being processed. One way to solve that is to force event processing at some point inside that method:

--- code.py 2011-12-05 10:32:53.000000000 +0100
+++ code.py.bak 2011-12-05 10:27:22.000000000 +0100
@@ -95,8 +95,6 @@
         timeout = 15
         while timeout != 0:
             self.set('l1','%s' % timeout)
-            while gtk.events_pending():
-                gtk.main_iteration()
             wait(1)
             timeout -= 1
             print timeout
Share:
13,056
Brian Bruggeman
Author by

Brian Bruggeman

I write code for a living now, though I graduated with a degree in electrical engineering. I have some holes in my education that I hope stack overflow can help fill.

Updated on June 05, 2022

Comments

  • Brian Bruggeman
    Brian Bruggeman over 1 year

    I have some code below that is attempting to update a GTK Label element. I'm including two files: the ui file and the py file.

    UI file:

    <glade-interface>
      <widget class="GtkWindow" id="ApplicationFrame">
        <property name="width_request">320</property>
        <property name="height_request">240</property>
        <property name="visible">True</property>
        <property name="events">GDK_KEY_PRESS_MASK</property>
        <property name="title" translatable="yes">Simple</property>
        <property name="resizable">False</property>
        <property name="window_position">center-always</property>
        <property name="default_width">320</property>
        <property name="default_height">240</property>
        <property name="decorated">False</property>
        <property name="gravity">center</property>
        <child>
          <widget class="GtkFixed" id="layout">
            <property name="width_request">320</property>
            <property name="height_request">240</property>
            <property name="visible">True</property>
            <child>
              <widget class="GtkLabel" id="l1">
                <property name="width_request">320</property>
                <property name="height_request">40</property>
                <property name="visible">True</property>
                <property name="xalign">0</property>
                <property name="label" translatable="yes">l1</property>
              </widget>
              <packing>
                <property name="y">43</property>
              </packing>
            </child>
          </widget>
        </child>
      </widget>
    </glade-interface>
    

    Python File

    import os
    from time import sleep as wait
    import gtk as gtk
    import gtk.glade as glade
    import gobject
    
    class Application(object):
        def __init__ (self):
            self.glade = glade.XML("simple.ui")
            self.setup_ui()
    
        def setup_ui (self):
            self.window = self.glade.get_widget("ApplicationFrame")
            self.l1 = self.glade.get_widget("l1")
            self.names = {'l1' : self.l1}
            self.all = [self.l1]
            gobject.timeout_add(1000,self.display_me)
            gobject.timeout_add(100,self.do_something_that_takes_a_while)
            self.window.add_events(gtk.gdk.KEY_PRESS_MASK)
            self.window.connect("key-press-event", self.handler)
            self.window.connect("delete_event", self.delete_event)
            self.window.connect("destroy", self.destroy)
            self.window.show()
    
        def get_signal (self,widget,event):
            keyname = gtk.gdk.keyval_name(event.keyval)
            ctrl = event.state & gtk.gdk.CONTROL_MASK
            alt = event.state & gtk.gdk.MOD1_MASK
            shift = event.state & gtk.gdk.SHIFT_MASK
            name = []
            if ctrl and keyname not in ["Control_L","Control_R"]: 
                name.append("CTRL")
            if alt and keyname not in ["Alt_L","Alt_R"]:
                name.append("ALT")
            if shift and keyname not in ["Shift_L","Shift_R"]:
                name.append("SHIFT")
            name.append(keyname)
            name = "+".join(name)
            return name
    
        def handler (self,widget,event):
            name = self.get_signal(widget,event)
            if name.lower() in ['ctrl+x','ctrl+c','alt+q']:
                self.destroy()
    
        def main(self):
            gtk.main()
    
        def delete_event (self,widget=None,event=None,data=None):
            return False
    
        def destroy (self,widget=None,data=None):
            gtk.main_quit()
    
        def get (self,item):
            if isinstance(item, str): 
                if item in self.names:
                    item = self.names[item]
            retval = None
            if hasattr(item,"text"):
                retval = item.text()
            elif hasattr(item,"get_label"):
                retval = item.get_label()
            return retval
    
        def set (self,item,text='',font=None):
            print 'Setting...'
            if isinstance(item, str): 
                if item in self.names:
                    item = self.names[item]
            retval = None
            if font == None and hasattr(self,"page") and hasattr(self.page,"NORMAL_FONT"): 
                    font = self.page.NORMAL_FONT
            if hasattr(item,"setText"):
                retval = item.setText(text)
            elif font == None:
                if hasattr(item,'set_text'):
                    retval = item.set_text(text)
            elif hasattr(item,"set_label"):
                retval = item.set_label(text)
                if hasattr(item,"modify_font") and font != None:
                    item.modify_font(font)
            item.queue_draw()
            self.try_to_update(item)
            print 'done'
            return retval
    
        def try_to_update (self,item):
            """
            do something here to update the visual screen????
            """
            print str(self)
    
        def do_something_that_takes_a_while (self):
            timeout = 15
            while timeout != 0:
                self.set('l1','%s' % timeout)
                wait(1)
                timeout -= 1
                print timeout
            return 1
    
        def clean (self):
            if item != None:
                self.set(item,empty_text)
            else:
                if hasattr(self,'all'):
                    for item in self.all:
                        self.set(item)
    
        def display_me (self):
            print str(self)
            return True
    
        def __str__ (self):
            space = 25
            value = '%'+str(space)+'s'
            lines = ['\n','-'*79]
            if hasattr(self,'all'):
                line = []
                for obj in self.all:
                    obj_value = self.get(obj)
                    line.append(value % obj_value if obj_value != None else '')
                    #line.append(value % ' ')
                lines.append(''.join(line))
            lines.append('-'*79)
            return '\n'.join(lines)
    
    
    if __name__ == "__main__":
        from time import sleep as wait
        SEC = 1
        app = Application()
        app.main()
    

    This should be simple, but I'm completely missing what I'm doing wrong. The element, l1, is not getting updated properly. I think the code within try_to_update is where I need to update the UI, but I'm at a loss as to which function I need to call. Can someone please help?

    Thanks in advance!

  • Brian Bruggeman
    Brian Bruggeman almost 12 years
    That's on line 83. :( Didn't work. Also, item does have the attribute 'show' so that should execute... I originally had item.show there. But I changed it because I had read the same functionality you did in the documentation. I've removed the if statement for clarity.
  • jro
    jro almost 12 years
    Ok. Try to call while gtk.events_pending(): gtk.main_iteration_do(True) in your update function; perhaps the event queue isn't processed when you expect it.
  • Cloud
    Cloud over 9 years
    Thank you. That worked wonderfully. I have a function now that calls gtk_main_iteration_do() after gtk_widget_queue_draw() in C.