Python Button "blinken" lassen mit tkinter

SirFrancisDrake

Cadet 4th Year
Registriert
Feb. 2014
Beiträge
115
Hi,

bin neu bei Python und probiere ein paar Sachen aus fuer eine GUI.

Python:
from tkinter import *
import time

# creating basic window
window = Tk()
window.geometry("500x375")  # size of the window width:- 500, height:- 375
window.resizable(0, 0)  # this prevents from resizing the window
window.title("Keyboard")

################################### functions ######################################
# 'btn_click' function continuously updates the input field whenever you enters a number

def btn_click(item):
    global expression
    expression = expression + str(item)
    input_text.set(expression)

# 'btn_clear' function clears the input field
def btn_clear():
    global expression
    expression = ""
    input_text.set("")


expression = ""
# 'StringVar()' is used to get the instance of input field
input_text = StringVar()


# creating a frame for the input field
input_frame = Frame(window, width = 312, height = 50, bd = 0, highlightbackground = "black", highlightcolor = "black", highlightthickness = 1)
input_frame.pack(side = TOP)


# creating a input field inside the 'Frame'
input_field = Entry(input_frame, font = ('arial', 18, 'bold'), textvariable = input_text, width = 50, bg = "#eee", bd = 0, justify = RIGHT)
input_field.grid(row = 0, column = 0)
input_field.pack(ipady = 10) # 'ipady' is internal padding to increase the height of input field


# creating another 'Frame' for the button below the 'input_frame'
btns_frame = Frame(window, width = 312, height = 272.5, bg = "grey")
btns_frame.pack()


# first row
button_a = Button(btns_frame, text = "A", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_click("A"))
button_a.grid(row = 0, column = 1, padx = 1, pady = 1)
button_b = Button(btns_frame, text = "B", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_click("B")).grid(row = 0, column = 2, padx = 1, pady = 1)
button_del = Button(btns_frame, text = "Del", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_clear()).grid(row = 0, column = 3, padx = 1, pady = 1)

#while True:
#    button_a.config(bg = 'yellow')
#    time.sleep(1)
#    button_a.config(bg = 'black')

window.mainloop()

Nun wuerde ich gerne den button_a "blinken" lassen. Jedoch sobald ich die while True Schleife aktiviere, wird mir gar keine GUI mehr angezeigt. Wie muss ich die Schleife in das Programm schreiben, so dass mir meine GUI noch angezeigt wird und mein button_a blinkt?
Vielleicht ist das eine dumme Frage, aber ich stehe im Moment wirklich auf dem Schlauch.

Viele Gruesse

SFR
 
bin nicht mit Tkinter vertraut, aber kurzes googlen sagt dass
Code:
window.mainloop()
die GUI "startet"
Wenn du in die Endlossschleife vorher gehst, wird das nichts :)

EDIT:
ich seh gerade, mainloop() "hält" das Programm an und ermöglicht keine Code-Ausführungen, wenn etwas danach steht.
Mir fallen spontan 3 Möglichkeiten ein, das Problem zu fixen.
- Mehrere Threads verwenden, einen für die GUI Erstellung, einen für weitere Code-Ausführungen
- eigene mainloop() bauen mit den Tkinter Methoden update() / update_idletasks()
-
die Tkinter Methode after() verwenden

- Möglichkeit 1 rate ich von ab, ist den Aufwand nicht wert und nicht ganz trivial.
- Möglichkeit 2 würde ich verwenden, wenn mehrere solcher GUI Events passieren, da kann man sich die mainloop quasi selbst zusammenbauen und alles "kontrollieren".
- Möglichkeit 3 ist die Einfachste. Du bastelst dir eine Methode, die die Farbe des Buttons ändert, und nach einer kurzen Pause sich selbt aufruft. Durch after() wir die Ausführung um x Millisekunden verzögert, in meinem Beispiel um eine Sekunde.

(Code ist nicht getestet und kann Fehler enthalten :) )

Python:
def btn_blink():
    global button_a, button_color
    if button_color == "black":
        button_a.config(bg = 'yellow')
    if button_color == "yellow":
        button_a.config(bg = 'black')
    time.sleep(1)
    btn_blink()

window.after(1000, btn_blink)
window.mainloop()
Link zu Doku:
http://epydoc.sourceforge.net/stdlib/Tkinter.Misc-class.html#after
 
Zuletzt bearbeitet:
threads oder timer tun aber auch nicht weh:

Python:
from tkinter import *
import threading
import time

# creating basic window
window = Tk()
window.geometry("500x375")  # size of the window width:- 500, height:- 375
window.resizable(0, 0)  # this prevents from resizing the window
window.title("Keyboard")

################################### functions ######################################
# 'btn_click' function continuously updates the input field whenever you enters a number

def btn_click(item):
    global expression
    expression = expression + str(item)
    input_text.set(expression)

# 'btn_clear' function clears the input field
def btn_clear():
    global expression
    expression = ""
    input_text.set("")

def blink_thread():
    while True:
        button_a.config(bg = 'yellow')
        time.sleep(1)
        button_a.config(bg = 'black')
        time.sleep(1)

def blink_timer(color):
    button_a.config(bg = color)
    threading.Timer(1, blink_timer, args=('yellow' if color is 'black' else 'black', )).start()

expression = ""
# 'StringVar()' is used to get the instance of input field
input_text = StringVar()


# creating a frame for the input field
input_frame = Frame(window, width = 312, height = 50, bd = 0, highlightbackground = "black", highlightcolor = "black", highlightthickness = 1)
input_frame.pack(side = TOP)


# creating a input field inside the 'Frame'
input_field = Entry(input_frame, font = ('arial', 18, 'bold'), textvariable = input_text, width = 50, bg = "#eee", bd = 0, justify = RIGHT)
input_field.grid(row = 0, column = 0)
input_field.pack(ipady = 10) # 'ipady' is internal padding to increase the height of input field


# creating another 'Frame' for the button below the 'input_frame'
btns_frame = Frame(window, width = 312, height = 272.5, bg = "grey")
btns_frame.pack()


# first row
button_a = Button(btns_frame, text = "A", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_click("A"))
button_a.grid(row = 0, column = 1, padx = 1, pady = 1)
button_b = Button(btns_frame, text = "B", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_click("B")).grid(row = 0, column = 2, padx = 1, pady = 1)
button_del = Button(btns_frame, text = "Del", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_clear()).grid(row = 0, column = 3, padx = 1, pady = 1)

# t = threading.Thread(target=blink_thread)
# t.daemon = True
# t.start()

threading.Timer(1, blink_timer, args=('yellow', )).start()

window.mainloop()
 
Vielen Dank fuer euere Antworten. Das Threading ist ne coole Loesung. Eine Frage haette ich noch. Warum kann ich den Blinker Thread nicht ueber alle Buttons laufen lassen? Fehlermeldung: "RuntimeError: main thread is not in main loop".

Habe das hier mal nachgelesen aber ganz verstehen tue ich es noch nicht:
Threads

Code:
[CODE=python]threading.Timer(1, blink_timer, args=('yellow',)).start()

Das start() am Ende steht fuer den Timer oder fuer die Mainloop? Und ist "threading.Timer" die Definition fuer den Thread?

Python:
from tkinter import *
import threading
import time

# creating basic window
window = Tk()
window.geometry("500x375")  # size of the window width:- 500, height:- 375
window.resizable(0, 0)  # this prevents from resizing the window
window.title("Keyboard")

################################### functions ######################################
# 'btn_click' function continuously updates the input field whenever you enters a number



def btn_click(item):
    global expression
    expression = expression + str(item)
    input_text.set(expression)

# 'btn_clear' function clears the input field
def btn_clear():
    global expression
    expression = ""
    input_text.set("")



#def blink_thread():
#    while True:
#        button_q.config(bg = 'yellow')
#        time.sleep(1)
#        button_q.config(bg = 'black')
#        time.sleep(1)

def blink_timer(color):
    buttons[i].config(bg = color)
    threading.Timer(1, blink_timer, args=('yellow' if color is 'black' else 'black', )).start()

expression = ""
# 'StringVar()' is used to get the instance of input field
input_text = StringVar()


# creating a frame for the input field
input_frame = Frame(window, width = 312, height = 50, bd = 0, highlightbackground = "black", highlightcolor = "black", highlightthickness = 1)
input_frame.pack(side = TOP)


# creating a input field inside the 'Frame'
input_field = Entry(input_frame, font = ('arial', 18, 'bold'), textvariable = input_text, width = 50, bg = "#eee", bd = 0, justify = RIGHT)
input_field.grid(row = 0, column = 0)
input_field.pack(ipady = 10) # 'ipady' is internal padding to increase the height of input field


# creating another 'Frame' for the button below the 'input_frame'
btns_frame = Frame(window, width = 312, height = 272.5, bg = "grey")
btns_frame.pack()


# first row
button_q = Button(btns_frame, text = "Q", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_click("Q"))
button_q.grid(row = 0, column = 1, padx = 1, pady = 1)
button_w = Button(btns_frame, text = "W", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_click("W"))
button_w.grid(row = 0, column = 2, padx = 1, pady = 1)

# second row

button_a = Button(btns_frame, text = "A", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_click("A"))
button_a.grid(row = 1, column = 1, padx = 1, pady = 1)
button_s = Button(btns_frame, text = "S", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_click("S"))
button_s.grid(row = 1, column = 2, padx = 1, pady = 1)

# third row

button_z = Button(btns_frame, text = "Z", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_click("Z"))
button_z.grid(row = 2, column = 1, padx = 1, pady = 1)
button_x = Button(btns_frame, text = "X", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_click("X"))
button_x.grid(row = 2, column = 2, padx = 1, pady = 1)

# fourth row

button_del = Button(btns_frame, text = "Del", fg = "black", width = 10, height = 3, bd = 0, bg = "#eee", cursor = "hand2", command = lambda: btn_clear())
button_del.grid(row = 3, column = 3, padx = 1, pady = 1)

# t = threading.Thread(target=blink_thread)
# t.daemon = True
# t.start()

buttons = [button_q, button_w, button_a, button_s, button_z, button_x]

for i in range (1, 6):
    threading.Timer(1, blink_timer, args=('yellow',)).start()

window.mainloop()
 
Zuletzt bearbeitet:
SirFrancisDrake schrieb:
Python:
threading.Timer(1, blink_timer, args=('yellow',)).start()
Das start() am Ende steht fuer den Timer oder fuer die Mainloop? Und ist "threading.Timer" die Definition fuer den Thread?

das start() gehört zum Timer().

deine for schleife mit range(1, 6) geht über 5 elemente, nicht alle 6. zudem ist der index des ersten elements einer liste "0". (also for i in range(0, 6))

sollen alle buttons blinken, dann übergibt der blink-funktion doch einfach die liste der buttons:

Python:
def blink_timer(bottuns, color):
    for button in buttons:
        button.config(bg = color)
    threading.Timer(1, blink_timer, args=(buttons, 'yellow' if color is 'black' else 'black')).start()
   
    ...
   
   
buttons = [button_q, button_w, button_a, button_s, button_z, button_x]

threading.Timer(1, blink_timer, args=(buttons, 'yellow')).start()
 
Zurück
Oben