[teilweise gelöst] Wann wird ein Window neu gezeichnet?

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
Groucho
User
Beiträge: 9
Registriert: Donnerstag 15. Februar 2007, 14:44

Hallo,

auf Button-druck soll sich das Hintergrundfenster für eine 1 Sekunde rot färben und dann wieder die vorherige Farbe annehmen.

Tatsächlich wartet das Programm aber erst eine Sekunde und färbt dann rot & setzt sofort wieder auf die vorherige Farbe. Dass der Farbwechsel stattfindet, sieht man, wenn die Zeile mit self.win.set_style(save_style) auskommentiert wird.

Der Befehl self.win.queue_draw() reicht offenbar nicht aus, um das Rot-Färben des Fensters vor dem sleep()-Befehl zu erzwingen. Wie kann ich hier vorgehen?

Code: Alles auswählen

import pygtk
pygtk.require ("2.0")
import gtk

from time import sleep

class Test:
    def __init__(self):
        self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.win.connect("destroy", lambda w: gtk.main_quit())
        self.win.set_default_size(500,400)
        
        vbox = gtk.VBox(False, 0)
        self.win.add(vbox)

        label = gtk.Label(u"Drücke Button für rotes Fenster!")
        vbox.pack_start(label,True,True,20)

        button = gtk.Button("Werde Rot!")
        button.connect("clicked",self.on_button_clicked)
        vbox.pack_start(button,False,False,20)
                        
        self.win.show_all()

    def on_button_clicked(self, widget):
        save_style = self.win.get_style()
        
        color_red = self.win.get_colormap().alloc_color("red")
        
        self.win.modify_bg(gtk.STATE_NORMAL,color_red)
        self.win.queue_draw()
        
        sleep(1.0)
        
        # nächste Zeile auskommentieren und den Wechsel auf Rot zu sehen
        self.win.set_style(save_style)

#----------------------------------------------------------------      
if __name__ == '__main__':
    Test()
    gtk.main()      
Zuletzt geändert von Groucho am Montag 5. März 2007, 15:44, insgesamt 1-mal geändert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ich würde kein ``sleep`` verwenden, sondern stattdessen ``gobject.timeout_add`` welches in 1 Sekunde die Farbe des Widgets wieder zurücksetzt.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Groucho
User
Beiträge: 9
Registriert: Donnerstag 15. Februar 2007, 14:44

Gute Idee, Leonidas :)

allerdings, wenn ich

Code: Alles auswählen

    def on_button_clicked(self, widget):
        save_style = self.win.get_style()
        color_red = self.win.get_colormap().alloc_color("red")
        self.win.modify_bg(gtk.STATE_NORMAL,color_red)
        self.source_id = gobject.timeout_add(1000, self.reset_style,save_style)
        
    def reset_style(self,style):
        self.win.set_style(style)
       
        gobject.source_remove(self.source_id)
        self.source_id = 0
        return False 
versuche, wird das Fenster nur beim ersten Klick rot, bei allen folgenden Buttonclicks bleibt es grau.

Probleme gibt's auch, wenn ich in der Sekunde nochmal auf den Button klicke, da dann mehrere reset_style-Funktionen in die timeout-Liste gehängt werden.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ich habe den Code etwas aufgeräumt und versucht das zu lösen, aber komme im Moment nicht dahinter. Ich glaube da muss man im Internet schauen oder selbst in der PyGTK-ML fragen müssen.

Aufgeräumter Code (mit Encoding und ohne unnötigem Lambda):

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: latin-1 -*-
import gtk, gobject

class Test(object):
    def __init__(self):
        self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.win.connect("destroy", gtk.main_quit)
        self.win.set_default_size(500, 400)
       
        vbox = gtk.VBox(False, 0)
        self.win.add(vbox)

        label = gtk.Label(u"Drücke Button für rotes Fenster!")
        vbox.pack_start(label, True, True, 20)

        button = gtk.Button("Werde Rot!")
        button.connect("clicked", self.on_button_clicked)
        vbox.pack_start(button, False, False, 20)
        
        self.win.show_all()
        
        self.saved_style = self.win.get_style()
        self.color_red = self.win.get_colormap().alloc_color("red")

    def on_button_clicked(self, widget):
        self.win.modify_bg(gtk.STATE_NORMAL, self.color_red)
        
        self.source_id = gobject.timeout_add(1000, self.reset_style)
        
    def reset_style(self):
        self.win.set_style(self.saved_style)
        return False

if __name__ == '__main__':
    Test()
    gtk.main()
Das Timeout ist doppelt Problem ist einfach zu lösen, du machst einfach eine Variable die anzeigt ob ein Timeout läuft (``True``) oder nicht (``False``). Die wird dann in ``__init__`` auf ``False gesetzt`` und in ``on_button_clicked`` geprüft und gesetzt und in ``reset_style`` wieder auf ``False`` gesetzt.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Groucho
User
Beiträge: 9
Registriert: Donnerstag 15. Februar 2007, 14:44

Nach einigem Stöbern in den pygtk-FAQs konnte ich ein Problem lösen und das andere zumindest umgehen:

1) Das Problem, dass das Fenster vor einem time.sleep()-Befehl nicht neu gezeichnet wird, lässt sich durch Einfügen der Zeilen:

Code: Alles auswählen

while gtk.events_pending():
            gtk.main_iteration(False)
lösen. Das GTK erledigt dann seine Zeichenaufgaben bevor es in Schlaf fällt.

2) Anscheinend ist bekannt, dass nach dem Aufrufen von get_style() und späterem set_style() der Befehl modify_bg() nichts mehr ändert (siehe http://www.mail-archive.com/pygtk@daa.c ... 06320.html ). Den Grund für dieses Verhalten konnte ich allerdings nicht finden.

Das Problem lässt sich jedoch umgehen, indem nicht der gesamte gtk.Style gespeichert und wiederhergestellt wird, sondern nur die Größe, die tatsächlich verändert wird (s. Zeilen 25 bzw. 34):

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: latin-1 -*-

import gtk, gobject

class Test(object):
    def __init__(self):
        self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.win.connect("destroy", gtk.main_quit)
        self.win.set_default_size(500, 400)
       
        vbox = gtk.VBox(False, 0)
        self.win.add(vbox)

        label = gtk.Label(u"Drücke Button für rotes Fenster!")
        vbox.pack_start(label, True, True, 20)

        button = gtk.Button("Werde Rot!")
        button.connect("clicked", self.on_button_clicked)
        vbox.pack_start(button, False, False, 20)
       
        self.win.show_all()
       
        self.original_color = self.win.get_style().bg[gtk.STATE_NORMAL]
        self.color_red = self.win.get_colormap().alloc_color("red")
        
    def on_button_clicked(self, widget):
        self.win.modify_bg(gtk.STATE_NORMAL, self.color_red)
       
        self.source_id = gobject.timeout_add(1000, self.reset_style)
       
    def reset_style(self):
        self.win.modify_bg(gtk.STATE_NORMAL, self.original_color)
        return False

if __name__ == '__main__':
    Test()
    gtk.main()
Antworten