Hallo
Für eines meiner Programme würde ich gerne Buttonklicks simulieren. Das heißt, dass der Button (wie bei einem Klick mit der linken Maustaste) der Button heruntergedrückt wird, und dann wieder zurückspringt...
Suche im Internet und hier im Forum hat keine Ergebnisse gebracht, obwohl ich dachte schon mal bei einer anderen Suche soetwas gelesen zu haben... Kann sein, dass es "Tastenklicks simulieren" war, was ich damals las.
Viele Grüße Markus
Buttonklick simulation
Hallo Markus12
Was es nicht gibt macht man selber.
Hier meine Variante basierend auf einem Canvas-Widget:
Gruss wuf
Was es nicht gibt macht man selber.
Hier meine Variante basierend auf einem Canvas-Widget:
Code: Alles auswählen
# Skriptname button_click_sim_02.py (wuf)
import Tkinter as tk
class MyButton(tk.Canvas):
def __init__(self, parent, text='MyButton', bd=1, relief='raised',
highlightthickness=0, **options):
tk.Canvas.__init__(self, parent, bd=bd, relief=relief,
highlightthickness=highlightthickness, **options)
self.width, self.height = options['width'], options['height']
self.bd = bd
self.create_text(self.width//2, self.height//2, text=text,
anchor='center', tags='text')
self.bind('<Button-1>', self.button_press)
self.bind('<ButtonRelease-1>', self.button_release)
self.state = False
def button_press(self, event=None):
if self.state: return
self.config(relief='sunken')
self.move('text', self.bd, self.bd)
self.state = True
def button_release(self, event=None):
if not self.state: return
self.config(relief='raised')
self.move('text', -self.bd, -self.bd)
self.state = False
def change_state(self):
if self.state:
self.button_release()
else:
self.button_press()
def button_click_simulation():
button.change_state()
app_win.after(500, button_click_simulation)
def button_callback():
print 'Button'
app_win = tk.Tk()
app_win.config(bg='steelblue3')
button = MyButton(app_win, width=200, height=30)
button.pack(padx=10, pady=10)
button_click_simulation()
app_win.mainloop()
Take it easy Mates!
Hallo wuf,
Du brauchst doch aber gar nicht den Button
mit einem Canvas simulieren.
Nimm doch direkt ein Button-Widget.
Damit ersparst Du Dir auch das "moven" des Textes.
http://paste.pocoo.org/show/116682/
yipyip
Du brauchst doch aber gar nicht den Button
mit einem Canvas simulieren.
Nimm doch direkt ein Button-Widget.
Damit ersparst Du Dir auch das "moven" des Textes.
http://paste.pocoo.org/show/116682/
yipyip
Hallo yipyip
Superlösung! Wusste gar nicht, dass sich die Text-Position auf dem Button rein durch das ändern der 'relief'-Option beeinflussen lässt. Das vereinfacht die Lösung natürlich.
Danke dir für den Tipp.
Gruss wuf
Superlösung! Wusste gar nicht, dass sich die Text-Position auf dem Button rein durch das ändern der 'relief'-Option beeinflussen lässt. Das vereinfacht die Lösung natürlich.
Danke dir für den Tipp.
Gruss wuf
Take it easy Mates!
Danke.
Soweit war ich aber auch schon
Mit after und allem dazugehörigen hatte es auch funktioniert, bis darauf, dass ich in einer Schleife der Funktion sagte, hintereinander die Buttons zu drücken und wieder loszulassen, aber da after einen Thread bildet, wurden alle Buttons zur gleichen Zeit heruntergedrückt und wieder losgelassen, was nicht dem Sinn entsprach. Auch dafür habe ich eine Lösung gefunden, aber die funktioniert nicht gut.
Und da ich dachte von einer Funktion gelesen zu haben, fragte ich hier nach
Meine Lösung:
Graphisch noch nicht gut, aber ich bin ja auch erst am Algorithmus dran
Komische Sache ist, dass wenn die Zeit in der after-Funktion größer ist als die in dem Thread, welcher time.sleep ausführt, dass es dann funktioniert, aber umgedreht nicht mehr... Da wird nicht einmal ein Button heruntergedrückt? Hängt das mit dem Thread zusammen?
Grüße Markus
Soweit war ich aber auch schon
Mit after und allem dazugehörigen hatte es auch funktioniert, bis darauf, dass ich in einer Schleife der Funktion sagte, hintereinander die Buttons zu drücken und wieder loszulassen, aber da after einen Thread bildet, wurden alle Buttons zur gleichen Zeit heruntergedrückt und wieder losgelassen, was nicht dem Sinn entsprach. Auch dafür habe ich eine Lösung gefunden, aber die funktioniert nicht gut.
Und da ich dachte von einer Funktion gelesen zu haben, fragte ich hier nach
Meine Lösung:
Code: Alles auswählen
class Zahlenfeld(Frame):
def __init__(self, master, **cnf):
self.kombination = []
self.buttons = {}
opt = {"pad": 0, "zpadx": 10, "zpady": 10, "zbg": "grey", "bd": 5, "font": ("Arial", 15, "bold")}
keys = opt.keys()
opt.update(cnf)
for i in keys:
setattr(self, i, opt[i])
del opt[i]
Frame.__init__(self, master, opt)
self.erstellen()
def erstellen(self):
row = 0
col = 0
for i in range(1, 10):
b = Button(self, font = self.font, text = i, padx = self.zpadx, pady = self.zpady, bg = self.zbg, bd = self.bd)
b.grid(row = row, column = col, padx = self.pad, pady = self.pad)
b.bind("<Button-1>", self._handler)
self.buttons[i] = b
if not (i%3):
row += 1
col = 0
else:
col += 1
def _handler(self, e):
self.hinzufuegen(e.widget.cget("text"))
def hinzufuegen(self, nummer):
self.kombination.append(nummer)
def wiederholen(self):
for i in self.kombination:
self._druecken(self.buttons[i], 1)
def _druecken(self, button, n):
if n:
button.config(relief="sunken")
button.after(600, lambda: self._druecken(button, 0))
thread = Thread(target = self._zeit)
thread.start()
thread.join()
else:
button.config(relief="raised")
button.update()
def _zeit(self):
time.sleep(0.5)
class Zahlenfenster(Tk):
def __init__(self, toolfenster = True, vordergrund = False, erscheinen = False, verschwinden = False):
self.toolfenster = bool(toolfenster)
self.vordergrund = bool(vordergrund)
self.erscheinen = bool(erscheinen)
self.verschwinden = bool(verschwinden)
self.tempo = 0.04
Tk.__init__(self)
self.title("Bitte geben sie ihre Kombination ein!")
self.attributes("-toolwindow", toolfenster)
self.attributes("-topmost", vordergrund)
self.protocol("WM_DELETE_WINDOW", self.schliessen)
self.resizable(0,0)
self.zahlenfeld = Zahlenfeld(self, pad=1, font=("Arial", 30, "bold"), bg="orange", zbg="white")
self.zahlenfeld.pack()
self.focus_force()
#self.mainloop()
if __name__ == "__main__":
a = Zahlenfenster(erscheinen = 1, verschwinden = 1, vordergrund = 1)
Komische Sache ist, dass wenn die Zeit in der after-Funktion größer ist als die in dem Thread, welcher time.sleep ausführt, dass es dann funktioniert, aber umgedreht nicht mehr... Da wird nicht einmal ein Button heruntergedrückt? Hängt das mit dem Thread zusammen?
Grüße Markus
@Markus12: Was veranstaltest Du bei `Zahlenfeld` bloss mit dem `opt`-Dictionary!? In Zeile 11 ist das garantiert *leer*, also als Argument überflüssig. Und den ganzen Inhalt dynamisch als Attribute an das Objekt binden, kann man auch einfacher und direkter haben. Sollte man aber nicht, denn letztendlich sind das ja *Argumente* die in `erstellen()` Verwendung finden. So sollte man sie da auch übergeben.
Die Aktion bei `Button`\s sollte man nicht mit `bind()` auf die Maustaste als Ereignis, sondern mit dem `command`-Argument machen, sonst verhalten sich die Schaltflächen nicht so, wie der Benutzer das gewohnt ist.
Das mit dem `Thread` ist eine total umständliche Art ``time.sleep(0.5)`` zu schreiben. Wenn man einen Thread startet und dann *sofort* danach solange wartet, bis er zuende ist, kann man sich den Thread auch sparen.
Letztlich sollte man das ohne Thread und ohne `sleep()`, nur mit `after()` lösen. Da muss man das "Denken in Schleifen" verlassen und bei jedem Aufruf durch `after()` überlegen welcher *eine* kleine Schritt zu tun ist, und nach diesem Schritt mit `after()` dafür sorgen, dass nach einer bestimmten Zeit der nächste Schritt getan wird.
`_druecken()` verwendet eine Zahl als Wahrheitswert, wo's doch extra `True` und `False` gibt.
Die Aktion bei `Button`\s sollte man nicht mit `bind()` auf die Maustaste als Ereignis, sondern mit dem `command`-Argument machen, sonst verhalten sich die Schaltflächen nicht so, wie der Benutzer das gewohnt ist.
Das mit dem `Thread` ist eine total umständliche Art ``time.sleep(0.5)`` zu schreiben. Wenn man einen Thread startet und dann *sofort* danach solange wartet, bis er zuende ist, kann man sich den Thread auch sparen.
Letztlich sollte man das ohne Thread und ohne `sleep()`, nur mit `after()` lösen. Da muss man das "Denken in Schleifen" verlassen und bei jedem Aufruf durch `after()` überlegen welcher *eine* kleine Schritt zu tun ist, und nach diesem Schritt mit `after()` dafür sorgen, dass nach einer bestimmten Zeit der nächste Schritt getan wird.
`_druecken()` verwendet eine Zahl als Wahrheitswert, wo's doch extra `True` und `False` gibt.
opt ist ein dictionary mit vordefinierten optionen, die das herkömmliche Frame nicht verwendet. Dieses dictionary wird zwei Zeilen später mit den übergebenen cnf-optionen überschrieben, denn es kann gut sein, dass beim initialisieren von Zahlenfeld optionen wie "pad" oder "zpadx" übergeben werden. Damit werden die von mir in opt vordefinierten werte überschrieben. und in variablen gespeichert. Gleichzeitig werden sie allerdings auch wieder aus opt gelöscht, denn meine eigenen optionen können nicht vom Frame-Widget gelesen werden und es würden Exceptions entstehen.
Für mich macht das Sinn, ich verstehe nicht, was du meinst. Es funktioniert, daher sollte es richtig sein.
mit bind habe ich wohl einfach geschlafen, mir ist nun, wo du's sagst bewusst, dass ich ´´command´´ verwenden sollte. Ich hatte es vergessen zu ändern, nachdem ich einen anderne Lösungsweg zuerst gegangen war, der nicht funktionierte, und da muss ich es wohl übersehen haben.
Keine ahnung wie man es sonst mit after löst. Habe zwei Beiträge oben drüber ja mein Problem geschildert gehabt, warum es nicht funktioniert, und keinen anderen Weg gefunden.
1 ist True und 0 ist False, und daher habe ich einfach die Zahlen verwendet anstatt die Wahrheitswerte, was auf's selbe rauskommt
Grüße Markus
Für mich macht das Sinn, ich verstehe nicht, was du meinst. Es funktioniert, daher sollte es richtig sein.
mit bind habe ich wohl einfach geschlafen, mir ist nun, wo du's sagst bewusst, dass ich ´´command´´ verwenden sollte. Ich hatte es vergessen zu ändern, nachdem ich einen anderne Lösungsweg zuerst gegangen war, der nicht funktionierte, und da muss ich es wohl übersehen haben.
Keine ahnung wie man es sonst mit after löst. Habe zwei Beiträge oben drüber ja mein Problem geschildert gehabt, warum es nicht funktioniert, und keinen anderen Weg gefunden.
1 ist True und 0 ist False, und daher habe ich einfach die Zahlen verwendet anstatt die Wahrheitswerte, was auf's selbe rauskommt
Grüße Markus
Ok ok ok, habe das wiederholen-Problem von alleine gelöst.
Neuer Code mit Änderungen soweit:
Grüße Markus
Neuer Code mit Änderungen soweit:
Code: Alles auswählen
from Tkinter import*
class Zahlenfeld(Frame):
def __init__(self, master, **cnf):
self.kombination = []
self.buttons = {}
opt = {"pad": 0, "zpadx": 10, "zpady": 10, "zbg": "grey", "bd": 5, "font": ("Arial", 15, "bold")}
keys = opt.keys()
opt.update(cnf)
for i in keys:
setattr(self, i, opt[i])
del opt[i]
Frame.__init__(self, master, opt)
self.erstellen()
def erstellen(self):
row = 0
col = 0
for i in range(1, 10):
b = Button(self, font = self.font, text = i, padx = self.zpadx, pady = self.zpady, bg = self.zbg, bd = self.bd)
b.grid(row = row, column = col, padx = self.pad, pady = self.pad)
b.bind("<Button-1>", self._handler)
self.buttons[i] = b
if not (i%3):
row += 1
col = 0
else:
col += 1
def _handler(self, e):
self.hinzufuegen(e.widget.cget("text"))
def hinzufuegen(self, nummer):
self.kombination.append(nummer)
def wiederholen(self):
self._druecken(0, True)
def _druecken(self, zahl, n):
if len(self.kombination) > zahl:
nummer = self.kombination[zahl]
button = self.buttons[nummer]
if n:
button.config(relief="sunken")
button.after(200, lambda: self._druecken(zahl, False))
else:
button.config(relief="raised")
button.after(300, lambda: self._druecken(zahl+1, True))
class Zahlenfenster(Tk):
def __init__(self, toolfenster = True, vordergrund = False, erscheinen = False, verschwinden = False):
self.toolfenster = bool(toolfenster)
self.vordergrund = bool(vordergrund)
self.erscheinen = bool(erscheinen)
self.verschwinden = bool(verschwinden)
self.tempo = 0.04
Tk.__init__(self)
self.title("Bitte geben sie ihre Kombination ein!")
self.attributes("-toolwindow", toolfenster)
self.attributes("-topmost", vordergrund)
self.protocol("WM_DELETE_WINDOW", self.schliessen)
self.resizable(0,0)
self.zahlenfeld = Zahlenfeld(self, pad=1, font=("Arial", 30, "bold"), bg="orange", zbg="white")
self.zahlenfeld.pack()
if self.erscheinen:
self.aufbauen()
self.focus_force()
#self.mainloop()
def aufbauen(self):
self.attributes("-alpha", 0.0)
self._aufbauen2()
def _aufbauen2(self):
alpha = self.attributes()[1]
if self.erscheinen and alpha < 1.0:
self.attributes("-alpha", alpha + self.tempo)
self.after(1, self._aufbauen2)
def schliessen(self):
self.attributes("-alpha", 1.0)
self._schliessen2()
def _schliessen2(self):
alpha = self.attributes()[1]
if self.verschwinden and alpha > 0.0:
self.attributes("-alpha", alpha - self.tempo)
self.after(1, self._schliessen2)
elif not self.verschwinden:
self.withdraw()
if __name__ == "__main__":
a = Zahlenfenster(erscheinen = 1, verschwinden = 1, vordergrund = 1)
@Markus12: `bind()` ist trotzdem der falsche Weg, wenn man `Button`\s haben möchte, die sich so verhalten, wie der Benutzer das erwartet. Und sich den Wert aus der Anzeige der GUI zu holen ist auch nicht besonders sauber.
Ok, danke für die kleine Hilfe.
Ich kenne allerdings keinen Ausweg, wie die Funktion herausfinden soll, welcher Button gedrückt wurde...
Ich habe ja nun die Sache mit dem Wiederholen gelöst, mich würde aber interessieren, wie du, Blackjack, es gelöst hättest
Viele Grüße Markus
Ich kenne allerdings keinen Ausweg, wie die Funktion herausfinden soll, welcher Button gedrückt wurde...
Ich habe ja nun die Sache mit dem Wiederholen gelöst, mich würde aber interessieren, wie du, Blackjack, es gelöst hättest
Viele Grüße Markus
Hallo Markus 12 !
So ganz habe ich nicht verstanden was du machen möchtest. Könnte dir das helfen ?
gruß frank
So ganz habe ich nicht verstanden was du machen möchtest. Könnte dir das helfen ?
Code: Alles auswählen
#! /usr/bin/env python
# -*- coding: utf-8
import Tkinter as tk
class Dialer(tk.Frame):
def __init__(self, master, font, number, width=2, height = 2, time = 300):
tk.Frame.__init__(self, master, width = width, height = height)
self.counter = 0
self.run = False
self.font = font
self.width = width
self.height = height
self.loop_time = time
self.number = number
self.button_press = True
self.dialing = True
self.buttons = list()
row = 0
col = 0
for i in xrange(9):
button = tk.Button(self, font = self.font, text = i+1)
button.grid(row = row, column = col, padx=3, pady=3)
self.buttons.append(button)
if col == 2:
col = -1
row += 1
col += 1
def dial_number(self):
if self.dialing:
if self.button_press:
self.buttons[self.number[self.counter]-1].config(relief = "sunken")
self.button_press = False
else:
self.button_press = True
self.buttons[self.number[self.counter]-1].config(relief = "raised")
self.counter += 1
if self.counter == len(self.number):
self.dialing = False
self.master.after(self.loop_time, self.dial_number)
class Gui(object):
FONT = "Arial 10 bold"
def __init__(self, root):
self.root = root
self.status_check = True
self.gui()
def gui(self):
self.dialer = Dialer(self.root, self.FONT, list((1, 4, 5, 9, 5, 6, 3)))
self.dialer.pack()
self.dialer.dial_number()
self.check_status()
def check_status(self):
if self.status_check:
if self.dialer.dialing == False:
self.dialing = False
print "END"
self.status_check = False
self.dialer.pack_forget()
self.root.after(10, self.check_status)
def main():
root = tk.Tk()
app = Gui(root)
root.resizable(0, 0)
root.title("Dialer")
root.mainloop()
if __name__ == '__main__':
main()
Danke kaytec,
Aber ich hatte mein Problem selbst gelöst
Danke, Blackjack, habe mir deinen code angesehen, und ja, habe noch eingebaut, dass man nicht während einer laufenden wiederholung eine zweite starten kann, danke für den tipp.
Grüße Markus
Aber ich hatte mein Problem selbst gelöst
Danke, Blackjack, habe mir deinen code angesehen, und ja, habe noch eingebaut, dass man nicht während einer laufenden wiederholung eine zweite starten kann, danke für den tipp.
Grüße Markus