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()