PermanentButton - dauerhaftes Ausführen einer Funktion

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

Hallo Forum,

für ein kleines Visualisierungsprogramm benötige ich (Python-Einsteiger) Buttons, bei denen eine gewünschte Aktion ausgeführt wird, solange der jeweilige Button gedrückt gehalten wird. Ansonsten soll sich der Button wie die vorhandenen gtk-buttons verhalten.

Nach einiger Suche im Netz und hier im Forum habe ich eine Klasse PermanentButton aufgestellt; der Rest des Programms dient nur der Illustration der Funktionalität.

Kritik und Verbesserungsvorschläge sind herzlich willkommen. Zwei konkrete Fragen sind mir beim Schreiben gekommen:

- die super()-Terminologie habe ich von Dookie aus den ColoredButtons in http://www.python-forum.de/topic-2682.html. Mir ist allerdings nicht klar, ob diese Schreibweise gegenüber einem direkten Bezug auf die Methoden von gtk.ToggleButton hier einen Vorteil bietet?

- die connect()-Methode von gtk.ToggleButton habe ich überladen und frage auf das neue Schlüsselwort "held_down" ab. Alle anderen Schlüsselwörter werden an gtk.ToggleButton weitergegeben. Macht das vom OOP-Standpunkt her Sinn, oder wäre es besser, nur "held_down" zuzulassen?

Gruß, Groucho

Code: Alles auswählen

#! /usr/bin/env python

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

from math import sin, cos, radians, pi

#----------------------------------------------------------------
class PermanentButton(gtk.ToggleButton):
    def __init__(self,*pargs,**kargs):
        super(PermanentButton,self).__init__(*pargs,**kargs)
        self.source_id = 0

    def connect(self,*pargs):   # connect() Methode überladen
        #  connect() mit Signal "held_down" klinkt die als erstem der
        #  folgenen Parameter übergebene Funkion in die Timeout-Liste ein.
        #  2. Parameter: Wiederholungsinterval in ms
        #  3. Parameter: eine Button-Id zur Identifizierung

        keyword, parametertupel = pargs[0],pargs[1:]  
        if keyword == "held_down":
            # wenn mit "held_down" verbunden, dann die Signale
            # "pressed" und "released" abfangen
            super(PermanentButton,self).connect("pressed",
                                                self.on_button_pressed,
                                                parametertupel)
            super(PermanentButton,self).connect("released",
                                                self.on_button_released)
        else: # ansonsten normales connect()
            super(PermanentButton,self).connect(*pargs)

    def on_button_pressed(self, button, parametertupel):
        function, interval, buttonid = parametertupel
        # function: die periodisch auszuführende Funktion
        # interval: Wiederholungsinterval in ms
        # buttonid: Kennung, anhand der der gehaltende Button in 'function'
        #           identifiziert werden kann

        # Funktion in die timeout-Liste aufnehmen        
        self.source_id = gobject.timeout_add(interval, function, buttonid)
        button.set_active(True)

    def on_button_released(self, button):
        # Funktion wieder aus der timeout-Liste entfernen
        gobject.source_remove(self.source_id)
        self.source_id = 0
        button.set_active(False)
        
#----------------------------------------------------------------
class MainWindow:
    def __init__(self):
        win = gtk.Window(gtk.WINDOW_TOPLEVEL)
        win.connect("destroy", lambda w: gtk.main_quit())
        win.set_title("Permanent Button Demo")
        win.set_default_size(300,200)

        # hbox enthält DrawingArea und vbox für Buttons
        hbox = gtk.HBox(False, 0)
        win.add(hbox)
        
        # DrawingArea
        self.drawing_area = gtk.DrawingArea()
        self.drawing_area.connect('realize',self.on_realize)
        self.drawing_area.connect('expose-event',self.on_expose_event)
        hbox.pack_start(self.drawing_area,expand=True,fill=True)      

        # Initialisiere Punkte zum Zeichen
        self.points = [(cos(i/7.*6*pi),sin(i/7.*6*pi)) for i in range(7)]
        
        # vbox für Buttons 
        vbox = gtk.VBox(True,0)
        hbox.pack_start(vbox,False,False,10)      
        
        # PermanentButtons, abgeleitet aus gtk.ToggleButton
        button = PermanentButton(label=" + ")
        # self.on_held_down wird alle 40 ms mit Button-Id "+" aufgerufen
        button.connect("held_down",self.on_held_down,40,"+")
        vbox.pack_start(button,False,False,0)
        
        button = PermanentButton(label=" - ")
        # self.on_held_down wird alle 40 ms mit Button-Id "-" aufgerufen
        button.connect("held_down",self.on_held_down,40,"-")
        vbox.pack_start(button,False,False,0)
        
        # Quit
        button = gtk.Button("Quit")
        button.connect_object("clicked",gtk.Widget.destroy,win)
        vbox.pack_start(button,False,False,0)
        
        win.show_all()
   
    def on_realize(self, widget):
        cm = widget.get_colormap()
        color_red = cm.alloc_color("red") 
        color_white = cm.alloc_color("white")

        self.drawable = widget.window
       
        self.gc1 = widget.window.new_gc()              
        self.gc1.set_foreground(color_white)

        self.gc2 = widget.window.new_gc()              
        self.gc2.set_foreground(color_red)
        self.gc2.set_line_attributes(2, gtk.gdk.LINE_SOLID,
                                    gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND)
        
    def on_expose_event(self, area, event):
        width, height = self.drawable.get_size()
        scale = min(width/2.5,height/2.5) 

        # Hintergrund weiß füllen
        self.drawable.draw_rectangle(self.gc1, True,1, 1, width-2, height-2)

        # Punkte auf Zeichenbereich transformieren und zeichnen
        transformed_points = [transform(i[0],i[1],width, height, scale, scale)
                             for i in self.points]            
        self.drawable.draw_polygon(self.gc2,False,transformed_points)

    #------------------------------------------------------------
    # on_held_down() wird ausgeführt, solange einer der PermanentButtons
    # gedrückt wird, die Buttons werden anhand der buttonid unterschieden
    def on_held_down(self, buttonid):
        if buttonid == "+":
            rotangle = radians(5)
        elif buttonid == "-":
            rotangle = -radians(5)

        # rotieren und neuzeichnen            
        self.points = [rot_allg(i[0],i[1],rotangle) for i in self.points]
        self.drawing_area.queue_draw()

        # Rückgabe True, damit Funktion erneut aufgerufen wird
        return True        
               
#----------------------------------------------------------------
def transform(x,y,width,height,xscale=1,yscale=-1):
    return(int(width/2.0+float(x)*xscale),int(height/2.0+float(y)*yscale))

def rot_allg(xi,xj,rad):
    return (cos(rad)*xi - sin(rad)*xj),(sin(rad)*xi + cos(rad)*xj)

#----------------------------------------------------------------      
if __name__ == '__main__':
    MainWindow()
    gtk.main()
        
Antworten