How to create a tkinter toggle button? - python

I've been working on a text editor using Tkinter in Python 2.7.
A feature that I'm trying to implement is the Night Mode, where the user can toggle between a black background and a light one, that switches from light to dark with a click of the toggle button.
from Tkinter import *
from tkSimpleDialog import askstring
from tkFileDialog import asksaveasfilename
from tkFileDialog import askopenfilename
from tkMessageBox import askokcancel
Window = Tk()
Window.title("TekstEDIT")
index = 0
class Editor(ScrolledText):
Button(frm, text='Night-Mode', command=self.onNightMode).pack(side=LEFT)
def onNightMode(self):
if index:
self.text.config(font=('courier', 12, 'normal'), background='black', fg='green')
else:
self.text.config(font=('courier', 12, 'normal'))
index = not index
However, on running the code, it is always in the night mode and the toggle doesn't work. Help.
Source Code: http://ideone.com/IVJuxX

You can import tkinter library (Use capital letter for python 2.7):
import Tkinter
Create tkinter objects...
root = tk.Tk()
...and tkinter button
toggle_btn = tk.Button(text="Toggle", width=12, relief="raised")
toggle_btn.pack(pady=5)
root.mainloop()
Now create a new command button called "toggle" in order to create the effect of "toggle" when you press playing on the relief property (sunken or raised) :
def toggle():
if toggle_btn.config('relief')[-1] == 'sunken':
toggle_btn.config(relief="raised")
else:
toggle_btn.config(relief="sunken")
At the end apply this behaviour on your button:
toggle_btn = tk.Button(text="Toggle", width=12, relief="raised", command=toggle)

The background and fg are set only in the if-clause. You need to set them also in the else clause:
def onNightMode(self):
if index:
self.text.config(font=('courier', 12, 'normal'), background='black', fg='green')
else:
self.text.config(font=('courier', 12, 'normal'))
index = not index
i.e.,
else:
self.text.config(font=('courier', 12, 'normal'), background='green', fg='black')

Here's a code snippet that will help you with the toggle button animation if you would like to. You only need to add the functions that you want to execute when clicking of course, that's up to you.
'''
import tkinter as tk
# --- functions ---
def move(steps=10, distance=0.1):
if steps > 0:
# get current position
relx = float(frame.place_info()['relx'])
# set new position
frame.place_configure(relx=relx+distance)
# repeate it after 10ms
root.after(10, move, steps-1, distance)
def toggle(event):
if button["text"] == "Yes":
move(25, 0.02) # 50*0.02 = 1
button["text"] = "No"
print("Clicked on yes")
elif button["text"] == "No":
move(25, -0.02)
button["text"] = "Yes"
print("Clicked on no")
# --- main --
root = tk.Tk()
frame = tk.Frame(root, background='red')
frame.place(relx=0, rely=0, relwidth=0.5, relheight=1)
# to center label and button
#frame.grid_columnconfigure(0, weight=1)
#frame.grid_rowconfigure(0, weight=1)
#frame.grid_rowconfigure(3, weight=1)
button = tk.Button(frame, text='Yes',width=5,height=1)
button.place(relx=0.25,rely=0.5,relwidth=0.5, relheight=0.1)
button.bind("<Button-1>",toggle)
root.mainloop()

Albe's answer is good but it has some bad coding practices.
Following the same steps:
Import Tkinter as tk
top = tk.TK()
Define your function here and make it work for any button, not hard coded to the specific button you might use.
def toggle(button: tk.Button):
if button.config('relief')[-1] == 'sunken':
button.config(relief="raised")
else:
button.config(relief="sunken")
Then create and pack all the toggle buttons you want.
toggleButton = tk.Button(text="Toggle", width=12, relief="sunken",
command =lambda:toggle(toggleButton))
toggleButton.pack(pady=5)
top.mainloop()
This is better for two reasons. Creating the button object twice is redundant and will lead to buggy code. Hard coding the button to a specific toggle function is unscalable. This solution makes the code reusable and simple to add to. For example, replace that last block with:
for _ in range(4):
b = tk.Button(text="Toggle", width=12, relief="sunken")
b['command']= lambda a=b:toggle(a)
b.pack(pady=5)
And now you get 4 toggling buttons without any additional functions or copy/paste

Related

how to change tkinter button for online few seconds

def click():
button1.configure(bg="gray")
time.sleep(1)
button1.configure(bg="green")
button1 = Button(win, text="Button",bg="green",activebackground="red")
button1.pack()
I tryied to change button to gray for only second than change back to green. But it won't change to gray
You need to bind that event after the packing
from tkinter import *
import time
def click(event):
if button1["background"] == "green":
time.sleep(3)
button1["background"] = "yellow"
else:
button1["background"] = "green"
root = Tk()
myContainer1 = Frame(root)
myContainer1.pack()
button1 = Button(myContainer1, text="Button", bg="green")
button1.pack()
button1.bind("<Button-1>", click) # binding event here
root.mainloop()
btw, solid resource on the subject, a bit dated but as an educational material - written perfectly - short :D
http://thinkingtkinter.sourceforge.net/
You have to do it this way.
If you use the Time library, the software won't work
or you can use the Threading module for multithreading in Python but This method is a bit complicated
from tkinter import *
def click(event):
def loop(i):
if i==1:
button1.configure(bg="gray")
elif i==2:
button1.configure(bg="red")
return
i=i+1
button1.after (1000, lambda : loop (i))
loop (1)
root = Tk()
myContainer1 = Frame(root)
myContainer1.pack()
button1 = Button(myContainer1, text="Button", bg="red")
button1.pack()
button1.bind("<Button-1>", click) # binding event here
root.mainloop()
First click() has never been executed. Second all updates are handled by tkinter mainloop(), so those changes will be handled after the function click() exits and the last change will be seen only.
You can use .after() instead of sleep() to change the color of the button back to green after one second:
def click():
button1.configure(bg="gray")
# change the background color back to green after 1 second
button1.after(1000, lambda: button1.configure(bg="green"))
button1 = Button(win, text="Button", bg="green", activebackground="red", command=click)
button1.pack()

on a button click, copy whats inside entry() and store it to a variable (tkinter)

I have two widgets to work with, a text input, and a button, both are created inside a function. What I want to happen is the user types in their name and then clicks the button to submit the answer. What I want the computer to do, is on the button press it will read whats inside the text and the save it to a variable. Once it saves it, it will print it out.
The code below is bad because it runs through the if statement immediately without the consulting of the button press.
There has to be a simpler solution. Also this may not be PEP 8 or whatever please be patient because I'm new.
import tkinter as tk
from tkinter import Tk, Label, Button
import sys
import time
import random
import threading
from tkinter import *
window = tk.Tk()
window.geometry("300x300")
window.title("GUI")
def start_screen():
reset()
start = tk.Label(window, text="start of game")
start.place(x=110,y=20)
play = Button(window, text= "play", command = start_game)
play.place(x=110,y=50)
helper = Button(window, text="help", command = help_screen)
helper.place(x=110,y=70)
def stuff():
global t
t = True
print(t)
return t
def text_handling():
global t
t = False
reset()#clears the screen
label = Label(window, text='')
question1= "what is your name?"
label.pack()
print_slow(label, question1, 40)#prints out letters slowly
#here is the part I'm having problems with
name = Entry(window)
name.pack()
but = Button(window, text="enter", command= stuff)
but.pack()
print(t)
if t == True:
myPlayer.name = name.get()
print(myPlayer.name)
def start_game():
reset()
bt = tk.Button(window,text="Enter", bg="orange", command =
text_handling)
bt.place(x=100,y=100)
start_screen()

tkinter Button printing same thing multiple times

I am currently working on a little just for fun project, which pretends it´s generating something and then shows a specific message, but I have a question: As soon as I press the button on the screen it is showing a progressbar, that is what I want it to do, but if I press the button again it just shows the same thing again and again, is there any way to prevent the program from printing the Starting the generate text and the progressbar multiple times?
Here´s the code:
# my little import area
import tkinter as tk
from tkinter import ttk
# Initialization
win = tk.Tk()
win.title("StackOverflow")
# Window Size
win.resizable(False, False)
win.minsize(750,500)
# Button clicked command
def buttonclicked():
tk.Label(win, text="Starting to generate...").pack()
pb.pack()
pb.start(500)
#Widgets
headerlabel = tk.Label(win, text="StackOverFlow Question")
generatebutton = tk.Button(win, text="Generate", command=buttonclicked)
pb = ttk.Progressbar(win, orient="horizontal", length=250, mode="determinate")
#Positioning
headerlabel.pack()
generatebutton.pack()
win.mainloop()
You can put
global generatebutton
generatebutton.config(state='disabled')
in your buttonclicked function (which is stupid because the global keyword is usually SO BAD to use, it turns your code into nightmare), or use OOP to your advantage. You can also use win.destroy() or generatebutton.destroy().
Here is that more OOP-intensive code example:
import tkinter as tk
from tkinter import ttk
class Joke:
def __init__(self):
self.win = tk.Tk()
self.win.title("StackOverflow")
self.win.resizable(False, False)
self.win.minsize(750,500)
headerlabel = tk.Label(self.win, text="StackOverFlow Question")
self.generatebutton = tk.Button(self.win, text="Generate", command=self.buttonclicked)
self.pb = ttk.Progressbar(self.win, orient="horizontal", length=250, mode="determinate")
headerlabel.pack()
self.generatebutton.pack()
self.win.mainloop()
def buttonclicked(self):
tk.Label(self.win, text="Starting to generate...").pack()
self.pb.pack()
self.pb.start(500)
self.generatebutton.config(state='disabled')
Joke()
Hope that's helpful!

How to change the visibility of a button in tkinter?

I'm new to python and I'm trying to create an application in which I want a button to be visible only after I click the "show" button. The button should not be visible from the start of application it should only be visible after clicking on "show" button.
I have this code which hides the button after clicking on another button. It changes its text to "show" after hiding the button.
from tkinter import *
root = Tk()
btn1 = Button(root,text="Example")
btn1.visible = True
btn1.place(x=20, y=50)
btn1.pi = btn1.place_info()
btn3 = Button(root, text="click me", command=lambda:plugin())
btn3.place(x=20, y=150)
def plugin():
master = Tk()
def toggle1():
if btn1.visible:
btnToggle1["text"] = "Show Example"
print ("Now you don't")
btn1.place_forget()
else:
btn1.place(btn1.pi)
print ("Now you see it")
btnToggle1["text"] = "Hide Example"
btn1.visible = not btn1.visible
btnToggle1 = Button(master, text="Hide Example", command=toggle1)
btnToggle1.place(x=70, y=150)
master.mainloop()
root.mainloop()
I want the button to show only after I click on the "show" button, not from the start.
At the risk of stating the obvious, if you don't want the button to be visible at startup, don't show the button at startup.
You're explicitly making the button visible with this line of code near the start of the program:
btn1.place(x=20, y=50)
You're then setting btn1.pi by calling place_info, but you don't need to do that. You can directly set btn1.p without first calling .place followed by place_info.
btn1 = Button(root,text="Example")
btn1.visible = False
btn1.pi = {"x": 20, "y": 50}
Notice that I also changed btn1.visible to False. You don't actually need a separate attribute to track if it's visible, tkinter can answer that question with the method winfo_viewable().
Or, you can simply remove the button after calculating btn1.pi:
btn1.place(x=20, y=50)
btn1.visible = False
btn1.pi = btn1.place_info()
btn1.place_forget()
I know this is not for the question I had a hard time finding this. If you are using the GTK library then
ButtonName.set_visible(False) sets it invisible
the below is for tkinter I just tested the below for tkinter:
from tkinter import *
def hide_me(event):
event.widget.pack_forget()
def make_invisible(widget):
widget.pack_forget()
root = Tk()
btn=Button(root, text="Click")
btn.bind('<Button-1>', hide_me)
btn.pack()
btn2=Button(root, text="Click too")
btn2.bind('<Button-1>', hide_me)
btn2.pack()
make_invisible(btn2)
root.mainloop()
the make_invisible(btn2) method sets it invisible however make sure to call it after you pack the button otherwise it will still show up.
here is my resources I used to get to this code
https://www.tutorialspoint.com/how-to-make-a-tkinter-widget-invisible
and https://newbedev.com/in-tkinter-is-there-any-way-to-make-a-widget-not-visible
i hope that this is what you mean:
from tkinter import *
import tkinter
root = Tk()
btn1 = Button(root,text="Example")
btn1.visible = True
btn1.place(x=20, y=50)
btn1.pi = btn1.place_info()
btn3 = Button(root, text="click me", command=lambda:plugin())
btn3.place(x=20, y=150)
def plugin():
master = Tk()
def toggle1():
if btn1.visible:
btnToggle1["text"] = "Show Example"
btnToggle1["state"] = DISABLED
print ("Now you don't")
btn1.place_forget()
else:
btn1.place(btn1.pi)
print ("Now you see it")
btnToggle1["state"] = NORMAL
btnToggle1["text"] = "Hide Example"
btn1.visible = not btn1.visible
btnToggle1 = Button(master, text="Hide Example", command=toggle1)
btnToggle1.place(x=70, y=150)
master.mainloop()
root.mainloop()

How to get the entry text and display in another entry?

I just want that when I type my name inside the entry box then appears in another entry with some add text. The idea is type in the entry below and after that it showed in the big entry.I was looking for this solution, but just found place in Label. I don't want in Label. The window is more big, must drag to show the entry. There's is a picture that i use in this script:
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
cat = Entry(root)
cat.place(x=48, y=25, width= 350, height=140)
user = Entry(root)
user.place(x=75, y=550)
btn = Button(root, text='START')
btn.place(x=220, y=410)
root.mainloop()
#
Ok, It works the way you told me,thank you!
But now i'm facing another problem.
The problem is when i insert the function of the game in the second window. I tested in one window and it works, but when i place the function in the second window gives an error when i press the "Start" button:
'''user_try = int(txt.get())
NameError: name 'txt' is not defined'''
When i press reset button gives another error:
'''user_try = int(txt.get())
NameError: name 'txt' is not defined'''
So i know that is missing definition, but i don't know how to make a reference for this command that it's in the second window. Like i said running with just one window the program works.
Maybe i should make using class, i don't know, but i wish to make this way that i started. However if there's no other way to do as i'm doing, let's go.
I just simplify the script here, actualy the main script is more bigger, so my idea is when open the program, there is a window and the user read the instructions about the game and proceed open the second window. The window have pictures and some hidden buttons in the next picture, so there will be an interactivity with the environment.
The guess number is just the beggining. After that there will be new challeges.
I'm very excited doing this, but i'm stuck in this point. The part one i finished, the pictures, the hidden buttons it's exacly the way i want, but the challenge stops here in this problem.
from tkinter import *
from PIL import Image, ImageTk, ImageSequence
import random
from tkinter import messagebox
pc = random.randint(1,10)
def reset():
global pc
pc = random.randint(1,10)
cat['text'] = 'Ok! Lets Try Again!'
def openwin2():
win1.withdraw()
win2 = Toplevel()
win2.geometry('350x300+180+100')
win2.title('second window')
txt = Entry(win2)
txt.place(x=10,y=10)
cat = Label(win2,wraplength=300)
cat.place(x=10,y=50)
cat.config(text='Hi! I Am thinking a number between 1 and 10.')
btn = Button(win2,text='start',command=check)
btn.place(x=30, y=150)
btn2 = Button(win2, text='reset', command=reset)
btn2.place(x=110,y=150)
win2.mainloop()
def check():
user_try = int(txt.get())
if user_try < pc:
msg = 'Hmmmm... the number, which I thought of, is greater than this.'
elif user_try > pc:
msg = 'How about trying a smaller number ?!'
elif user_try == pc:
msg = 'Well Done! You guessed! It was %s the number!' % user_try
else:
msg = 'Something Went Wrong...'
cat['text'] = msg
win1 = Tk()
win1.title('First Window')
win1.geometry('350x300')
user = Label(win1,text='first window')
user.place(x=10,y=10)
btn1 = Button(win1,text='Open Window 2', command=openwin2)
btn1.place(x=10,y=50)
win1.mainloop()
There are multiple ways to do this in tkinter, here's a rework of your code using StringVar objects set to the textvariable properties of your Entry objects:
import tkinter as tk
def doit():
out_string.set("Hello " + in_string.get())
root = tk.Tk()
in_string = tk.StringVar()
out_string = tk.StringVar()
cat = tk.Entry(root, textvariable=in_string)
cat.place(x=20, y=25, width=100)
user = tk.Entry(root, textvariable=out_string)
user.place(x=20, y=75)
btn = tk.Button(root, text='START', command=doit)
btn.place(x=20, y=150)
root.mainloop()
Per #Mike-SMT, here's a different approach using Entry.get() and Entry.insert(). It augments the text when the user clicks the button:
import tkinter as tk
def doit():
user.insert(tk.END, cat.get())
root = tk.Tk()
cat = tk.Entry(root)
cat.place(x=20, y=25, width=100)
user = tk.Entry(root)
user.place(x=20, y=75)
user.insert(0, "Hello ")
btn = tk.Button(root, text='START', command=doit)
btn.place(x=20, y=150)
root.mainloop()
However, you'll see that subsequent button clicks keep appending the text. When working with Entry.insert(), you need to work with Entry.delete() and/or other Entry methods to properly manipulate the text.

Categories

Resources