How to make two buttons still appear after closing the program and calling the function causes the addition of further buttons?
Here is the code:
from tkinter import *
win = Tk()
def add_button():
b2 = Button(win, text="click").grid()
b1 = Button(win, text="click", command=add_button).grid()
win.mainloop()
To save/restore the appeareance, in your case you need to save the number of buttons. The basic idea is:
Read the config file containing the number of buttons and create them (see restore_window() in the code below).
Let the users add as many buttons as they want, keeping track of how many buttons were added (in the number_of_btns variable).
Save the number of buttons in a file when the window is closed. To do so I used win.protocol("WM_DELETE_WINDOW", save_and_close) to execute save_and_close() when the user closes the window.
Here is the full code:
# from tkinter import * -> to be avoided because it may lead to naming conflicts
import tkinter as tk
number_of_btns = 0 # keep track of the number of buttons
def restore_window():
try:
with open("window_config.txt", "r") as file: # open file
nb = int(file.read()) # load button number
except Exception: # the file does not exist or does not contain a number
nb = 0
for i in range(nb):
add_button()
def save_and_close():
with open("window_config.txt", "w") as file:
# write number of buttons in file
file.write(str(number_of_btns))
win.destroy()
def add_button():
global number_of_btns # change value of number_of_btns outside the scope of the function
b2 = tk.Button(win, text="click").grid()
number_of_btns += 1
win = tk.Tk()
win.protocol("WM_DELETE_WINDOW", save_and_close) # execute save_and_close when window is closed
b1 = tk.Button(win, text="Add", command=add_button).grid()
restore_window()
win.mainloop()
Related
I'm trying to create a series of tkinter buttons with a loop that are .grid'd to their own respective frames. I want every button to have a function that .tkraises the next frame in the list of frames that I create. Any idea how? Here's what I've got. The buttons/ frames are created I think but the .tkraise function doesn't work. Thanks
from tkinter import *
from PIL import ImageTk, Image
## Define root and geometry
root = Tk()
root.geometry('200x200')
# Define Frames
winlist = list()
winlist = Frame(root, bg='red'), Frame(root, bg='green'), Frame(root, bg='blue')
# Configure Rows
root.grid_rowconfigure(0, weight = 1)
root.grid_columnconfigure(0, weight = 1)
# Place Frames
for window in winlist:
window.grid(row=0, column = 0, sticky = 'news')
# Raises first window 'To the top'
winlist[0].tkraise()
# Function to raise 'window' to the top
def raise_frame(window):
window.tkraise()
d = {}
count = 0
for x in range(0, 3):
d["label{0}".format(x)] = Label(winlist[x], text = "label{0}".format(x))
if count <=1:
try:
d["button{0}".format(x)] = Button(winlist[x], text = "button{0}".format(x), command = raise_frame(winlist[x+1]))
d["button{0}".format(x)].pack(side=TOP)
except:
pass
else:
d["label{0}".format(x)].pack(side=TOP)
count += 1
root.mainloop()
The issue is on the command option of the line:
d["button{0}".format(x)] = Button(winlist[x], text = "button{0}".format(x), command = raise_frame(winlist[x+1]))
It will execute raise_frame(winlist[x+1]) immediately and then assign the result (which is None) to command option. Therefore, clicking the button later does nothing.
You need to use lambda instead:
d["button{0}".format(x)] = Button(winlist[x], text="button{0}".format(x),
command=lambda x=x: raise_frame(winlist[x+1]))
I answered my own question. instead of using frames I went back to creating Tk() objects. I made a loop that runs a function that creates Tk() objects and passed in a variable that carried the count of my loop. I used that count to change information on each Tk() object and instead made the 'command =' of each button include a Tk().destroy function. This creates all the windows I wanted all at once and I can perform an action and exit the window. It's progress. Thanks,
Tim,
I am trying to make a program which enables comparison of data from selected files in one graph. The order in which they are depcited as well as the distance between them is important. However the order in which I select them in the window created by using Tkinter is not the same as the order in which they are passed to the program. The order in which I have to select them in order to get the correct order is 2, 3, 4, 5, 1 this results in order 1, 2, 3, 4, 5 in the program.
It is not a big problem once you know it but the intention is that others will be using the program as well so I need it to work as simple and robust as possible. Below is the snippet of the code that I use where Tkinter is involved.
root = Tkinter.Tk()
filez = tkFileDialog.askopenfilenames(parent=root,title='Choose a file')
filez= root.tk.splitlist(filez)
root.destroy()
There are no options for controlling the order in listed in the documentation. Your best bet is to use multiple file selection dialogs one after another.
tkFileDialog doesn't have function to remeber order of selected files so you can build own FileDialog or...
... build some Dialog to select order of files after you get files from tkFileDialog
import Tkinter as tk
import tkFileDialog
def Selector(data):
def append(widget, element, results, display):
# append element to list
results.append(element)
# disable button
widget['state'] = 'disabled'
# add element to label
current = display['text']
if current:
current += '\n'
display['text'] = current + element
# create window
root = tk.Tk()
# list for correct order
results = []
# label to display order
tk.Label(root, text='ORDER').pack()
l = tk.Label(root, anchor='w', justify='left')
l.pack(fill='x')
# buttons to select elements
tk.Label(root, text='SELECT').pack()
for d in data:
b = tk.Button(root, text=d, anchor='w')
b['command'] = lambda w=b, e=d, r=results, d=l:append(w, e, r, d)
b.pack(fill='x')
# button to close window
b = tk.Button(root, text='Close', command=root.destroy)
b.pack(fill='x', pady=(15,0))
# start mainloop
root.mainloop()
return results
# --- main ---
root = tk.Tk()
filez = tkFileDialog.askopenfilenames(parent=root,title='Choose a file')
root.destroy()
print(filez)
filez = Selector(filez)
print(filez)
Im trying to make a little program that endlessly prints out numbers inside GUI window, I can not find a way to print the out put of the function in a text box inside the GUI window instead of the python shell, please help, here is my code so far...
import sys
from tkinter import *
root = Tk()
def number(event):
x = 420
while True:
x +=420
print(x^70)
button_1 = Button(root, text="Start...")
button_1.bind("<Button-1>", number)
button_1.pack()
root.mainloop()
Thanks Harvey
You'll find it hard to constantly insert a value into a widget. The widget does not update with each insert. You can think of it has having a temporary variable for it. It can be accessed during the loop (as shown with print). However you'll notice that the widget itself doesn't update until the loop is over. So if you have while True then your widget will never update, and so you won't have the numbers streaming into the widget.
import sys
from tkinter import *
root = Tk()
def number():
x = 420
while x < 8400: # Limit for example
x +=420
textbox.insert(END, str(x^70)+'\n')
print(textbox.get(1.0, END)) # Print current contents
button_1 = Button(root, text="Start...", command=number) #Changed bind to command, bind is not really needed with a button
button_1.pack()
textbox = Text(root)
textbox.pack()
root.mainloop()
I'm trying to create a program in python using tkinter, and this program is supposed to have a list of books created by the user. On the main window (the one with the list), there should be a menubar with the option to add a book to the list. When clicked, this option should open another window, this time with one entrybox, where the user should enter the book's title and an add button, to add the button to the list.
The list is saved in a .txt file.
This is the program I wrote so far:
import sys
from tkinter import *
def newBook():
def add():
BookTitle = v.get()
bookTitle = '\n' + BookTitle
books = open('c:/digitalLibrary/books.txt', 'a')
books.write(bookTitle)
books.close()
addWindow = Tk()
v = StringVar()
addWindow.geometry('250x40+500+100')
addWindow.title('digitalLibrary - Add Book')
newBookEntry = Entry(addWindow,textvariable=v)
newBookEntry.pack()
addButton = Button(addWindow, text='ADD', command=add)
addButton.pack()
def refresh():
books = open('c:/digitalLibrary/books.txt', 'r')
bookList = books.readlines()
books.close()
for i in range (0, len(bookList)):
bookOne = Label(text=bookList[i])
bookOne.grid(row=i, column=0, sticky=W)
def quitProgram():
tfQuit = messagebox.askyesno(title='Close Program', message='Are you sure?')
if tfQuit:
window.destroy()
window = Tk()
menubar = Menu(window)
window.geometry('400x400+200+100')
window.title('digitalLibrary')
booksmenu = Menu(menubar, tearoff=0)
booksmenu.add_command(label='Add Book', command=newBook)
booksmenu.add_command(label='Delete Book')
booksmenu.add_command(label='Close Program', command=quitProgram)
menubar.add_cascade(label='digitalLibrary', menu=booksmenu)
books = open('c:/digitalLibrary/books.txt', 'r')
bookList = books.readlines()
books.close()
for i in range (0, len(bookList)):
bookOne = Label(window, text=bookList[i])
bookOne.grid(row=i, column=0, sticky=W)
refreshButton = Button(window, text='Refresh', command=refresh)
refreshButton.grid(row=0, column=1)
window.config(menu=menubar)
window.mainloop()
It seems logical to me that this should work, but it just doesn't. When I click the ADD button on the Add Book window, all it does is add the line break to the .txt file.
I know that it works if I use the OS library and create a separate python file for the add book window, but I'd rather put it all in one code, if possible.
I've tried many things, and tried searching it in the web, but I got nowhere.
The root cause of your problem is that you are creating more than once instance of Tk. You cannot do this. If you want to create a popup window, create an instance of Toplevel. A proper Tkinter application creates exactly once instance of Tk with exactly one invocation of mainloop.
If your main goal is to simply get input from the user (versus learning how to write your own dialog), you might want to consider using one of the built-in dialogs.
For example:
import tkinter.simpledialog as tkSimpleDialog # python 3.x
...
def newBook():
BookTitle = tkSimpleDialog.askstring("Add Book","What is the name of the book?")
if BookTitle is not None:
bookTitle = '\n' + BookTitle
books = open('/tmp/books.txt', 'a')
books.write(bookTitle)
books.close()
I made a very simple gui that has a button and shows an image(.gif). My goal is to output another .gif whenever you press the button. There are 2 .gif files in my file directory and the point is to keep switching between these two whenever you press the button.
#Using python2.7.2
import Tkinter
root = Tkinter.Tk()
try:
n
except:
n = 0
def showphoto(par):
if par%2 == 0:
try:
label2.destroy()
except:
pass
photo = Tkinter.PhotoImage(file="masc.gif")
label2 = Tkinter.Label(image=photo)
label2.image = photo
label2.pack()
else:
try:
label2.destroy()
except:
pass
photo = Tkinter.PhotoImage(file="123.gif")
label2 = Tkinter.Label(image=photo)
label2.image = photo
label2.pack()
myContainer1 = Tkinter.Frame(root, width = 100, height = 100)
myContainer1.pack()
def callback(event):
global n
showphoto(n)
n = n + 1
button1 = Tkinter.Button(myContainer1)
button1["text"]= "Next pic"
button1["background"] = "green"
button1.bind("<Button-1>", callback(n))
button1.pack()
root.mainloop()
The current code just outputs the first image (masc.gif) but when I press the button it doesn't switch to the other image(123.gif). What am I doing wrong?
This can achieved much easier with classes as the class holds all the data necessary without the use of global variables.
import Tkinter as tk
from collections import OrderedDict
class app(tk.Frame):
def __init__(self,master=None, **kwargs):
self.gifdict=OrderedDict()
for gif in ('masc.gif','123.gif'):
self.gifdict[gif]=tk.PhotoImage(file=gif)
tk.Frame.__init__(self,master,**kwargs)
self.label=tk.Label(self)
self.label.pack()
self.button=tk.Button(self,text="switch",command=self.switch)
self.button.pack()
self.switch()
def switch(self):
#Get first image in dict and add it to the end
img,photo=self.gifdict.popitem(last=False)
self.gifdict[img]=photo
#display the image we popped off the start of the dict.
self.label.config(image=photo)
if __name__ == "__main__":
A=tk.Tk()
B=app(master=A,width=100,height=100)
B.pack()
A.mainloop()
Of course, this could be done more generally ... (the list of images to cycle through could be passed in for example), and this will switch through all the images in self.gifs ...
This approach also removes the necessity to destroy and recreate a label each time, instead we just reuse the label we already have.
EDIT
Now I use an OrderedDict to store the files. (keys=filename,values=PhotoImages). Then we pop the first element out of the dictionary to plot. Of course, if you're using python2.6 or earlier, you can just keep a list in addition to the dictionary and use the list to get the keys.
button1 = Tkinter.Button(myContainer1)
button1["text"]= "Next pic"
button1["background"] = "green"
button1.bind("<Button-1>", callback(n))
First, you bind the <Button-1> event to None (that's what callback(n) evaluates to). You should bind it to callback (no parentheses a.k.a the call operator).
Second, I suggest you change callback to not accept any arguments, remove the bind call and create your button as:
button1 = Tkinter.Button(myContainer1, command=callback)