I'm trying to write a program that is a recipe book that lets you add recipes etc. I'm fairly new with Python and Tkinter though.
#New Recipe Screen
def click(key):
new_recipe = Tk()
new_recipe.title("New Recipe")
itemtext = Label(new_recipe, text="Item").grid(row=0, column=0)
input_item = Entry(new_recipe).grid(row=0, column=1)
quantitytext = Label(new_recipe, text="Quantity").grid(row=1, column=0)
input_quantity =Entry(new_recipe).grid(row=1, column=1)
unittext = Label(new_recipe, text="Unit").grid(row=2, column=0)
input_unit = Entry(new_recipe).grid(row=2, column=1)
fin_btn_text = "Finish"
def write(x=fin_btn_text):
click(x)
dataFile = open("StoredRecipes.txt", "w")
dataFile.write(str(input_item, ) + "\n")
new_recipe.destroy
finish_btn = Button(new_recipe, text=fin_btn_text, command=write).grid(row=3, column=0)
Two problems here:
You are not closing the file when you are done with it. Some systems require you to do this in order to commit the changes. Either call dataFile.close() at the end of your write function or use a with-statement to open the file (which will automatically close it when you are done):
def write(x=fin_btn_text):
click(x)
with open("StoredRecipes.txt", "w") as dataFile:
dataFile.write(str(input_item, ) + "\n")
new_recipe.destroy() # Remember to call this
As #Kevin noted in a comment, you cannot call .grid on the same line as you create a widget. The .grid method works in-place and always returns None. Thus, it should be called on its own line after you create the widget:
itemtext = Label(new_recipe, text="Item")
itemtext.grid(row=0, column=0)
Related
I have a simple GUI where the user selects a file, which becomes a variable for my main code. Here, my variable output should be the database path (gui_db_path) which the user inputs. When I run this code, called gui_test.py, the variable is printable, and prints to the console.
class GUI:
def __init__(self, window):
# 'StringVar()' is used to get the instance of input field
self.input_db_text = StringVar()
window.title("HyPep 1.0")
window.geometry("700x700")
ttk.Label(window, text='Database sequences .csv:').grid(row=1,column=0)
ttk.Button(window, text = "Browse", command = lambda: self.set_path_database_field()).grid(row = 1,column=2, ipadx=5, ipady=0)
ttk.Entry(window, textvariable = self.input_db_text, width = 70).grid( row = 1, column = 1, ipadx=1, ipady=1)
ttk.Button(window, text = "Analyze").grid(row = 10,column=1, ipadx=5, ipady=15)
def set_path_database_field(self):
self.path_db = askopenfilename()
self.input_db_text.set(self.path_db)
def get_database_path(self):
""" Function provides the database full file path."""
return self.path_db
if __name__ == '__main__':
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
print(gui.path_db, '\n', gui.path_qu)
gui_db_path = gui.path_db
print(gui_db_path)
My issue is that I need to retrieve this variable for use in another file, user_input.py, but is no longer callable. My code for user_input.py is:
from gui_test import gui_db_path
print(gui_db_path)
Instead of printing to the console in this instance, I get:
ImportError: cannot import name 'gui_db_path' from 'gui_test'
I'm sure there is a simple solution that I am missing, can anyone shed some light?
...
Update: much closer, need to expand the solution:
How would I go about expanding this to retrieve multiple paths? I have been trying this:
gui_test.py:
...
def get_db_path():
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
return gui.get_database_path()
def get_qu_path():
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
return gui.get_query_path()
user_input.py:
from gui_test import get_db_path
from gui_test import get_qu_path
gui_db_path = get_db_path()
gui_qu_path = get_qu_path()
Note that the code inside if __name__ == '__main__' block will not be executed when the file is imported. You need to put those code inside a function instead and returns the path at the end of the function:
gui_test.py
...
def get_db_path():
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
return gui.get_database_path()
Then import this function inside user_input.py:
from gui_test import get_db_path
gui_db_path = get_db_path()
print(gui_db_path)
First time posting to Stack! I am very new to Python / Tkinter so bear with me.
I am trying to write code that looks for a saved txt file upon startup that describes the path of a chosen XML file, and for it to print a tkinter label based on the chosen directory.
Problem I'm having is this: if you choose the XML file path, it will print the path - which is what I want. But, if you choose another XML file path, the previously printed label doesn't get destroyed, and the new label gets applied on top of the old one. I have looked at other people having similar issues and I'm stuck. I have tried a few different ways of implementing the .destroy() method, and none of them seem to be working. I wouldn't be surprised if there are other errors/redundancies in my code (please focus on the .destroy() issue), but it seems to be working the way I want other than this issue. I do not get any error messages with this code.
Here's what the code looks like right now (I only posted the part that is having the issue, let me know if you need more).
I have been testing this issue by selecting a long file path first, and then a shorter path on the desktop. The path on the desktop will print on top of the previous label instead destroying the previous label, seemingly ignoring the reset() function.
import xml.etree.ElementTree as ET
from tkinter import filedialog
from tkinter import *
import tkinter as tk
root= tk.Tk()
canvas1 = tk.Canvas(root, width = 750, height = 750)
canvas1.pack()
#Destroy previous label
global label_file
def reset():
label_file.destroy()
#Function that saves selected path
def findfile():
reset()
global file_selected
file_selected = filedialog.askopenfilename(filetypes=[("XML Files", "*.xml")])
if file_selected == "":
label_file = tk.Label(root, text= 'Error. Choose location of config.xml:', bg='red' )
canvas1.create_window(370, 165, window=label_file)
else:
label_file = tk.Label(root, text= 'Saved Path: '+ file_selected, bg='yellow' )
canvas1.create_window(370, 165, window=label_file)
savedpath = open("path.txt", "w")
savedpath.write(file_selected)
#Attempts to read save file on startup
try:
with open('path.txt', 'r') as f:
global file_selected
file_selected = f.readline()
label_file = tk.Label(root, text= 'Path: ' + file_selected, bg='#00ff00' )
canvas1.create_window(370, 165, window=label_file)
button1 = tk.Button (root, text='Change Path',command=findfile, bg='orange')
canvas1.create_window(50, 200, window=button1)
#If no save file on startup
except FileNotFoundError:
file_selected = ""
label_file = tk.Label(root, text= 'Choose location of config.xml file: ', bg='red' )
canvas1.create_window(168, 165, window=label_file)
button2 = tk.Button (root, text='Browse',command=findfile, bg='orange')
canvas1.create_window(50, 200, window=button2)
root.mainloop()
I write a code like this:
import tkinter as tk
from tkinter import *
def note1():
window = tk.Tk()
window.title("Not")
window.geometry("300x600+90+0")
notes = Entry(window,font="Verdana 14 italic")
notes.pack()
notesf = tk.Label(text=notes,font = "Verdana 10 italic" )
notesf.pack
window = tk.Tk()
window.title("Note")
window.geometry("1680x1050+90+0")
monhts = tk.Label(text="OCAK",font = "Verdana 50 italic")
monhts.pack()
day1= tk.Button(text="1 Ocak",font = "Verdana 30 italic",command=note1)
day1.place(x=75,y=250)
window.mainloop()
But my problem isn't with all code if you look at the Def line everything seems normal but I just want to save this entry like you just write x person will pay 10 dollars and you close the program after that. When you open it again it shouldn't disappear. I have just trying to solve this for a couple of hours but I still don't have an idea. Please help me.
Here we are writing entries:
Welcome to Stack Overflow!
In order to achieve this, you will need to save the contents in a text file and then retrieve them when required. Here is a working example of what you are looking for:
import tkinter as tk
from tkinter import *
def save(cont, win):
with open ('save.txt', 'w+') as file:
file.write(cont.get())
file.close()
win.destroy()
def retrieve(cont):
with open ('save.txt', 'r') as file:
data = file.read()
file.close()
return data
def note1():
window1 = tk.Toplevel()
window1.title("Not")
window1.geometry("300x600+90+0")
content = StringVar()
notes = Entry(window1, font="Verdana 14 italic", textvariable = content)
notes.pack()
try:
content.set(retrieve(content))
except:
pass
notesf = tk.Label(text=notes,font = "Verdana 10 italic" )
notesf.pack
window1.protocol("WM_DELETE_WINDOW", lambda cont = content, win = window1: save(cont, win))
window = tk.Tk()
window.title("Note")
window.geometry("1680x1050+90+0")
monhts = tk.Label(text="OCAK",font = "Verdana 50 italic")
monhts.pack()
day1= tk.Button(text="1 Ocak",font = "Verdana 30 italic",command=note1)
day1.place(x=75,y=250)
window.mainloop()
Notes:
I have attached the Entry to a StringVar(), by doing so, I can easily use the .get() and .set() method for getting and setting the content respectively.
You were having 2 instances of Tk() in the same mainloop(), which is not appreciated, make sure you have one Tk() in one mainloop() and the others as Toplevel().
I have used .protocol("WM_DELETE_WINDOW", ...) method on the window since you wanted the save to happen upon closing the window.
The approach that I have mentioned ensures your Entry will display the previously entered data even if you kill the parent window after saving, but if you like to not have the data after killing the parent window and only have it during the runtime, I suggest you to just make use of the StringVar and then set it's previous value, every time you click the button.
I'm a complete newbie. I started last week with python in which I'm trying to create a functional GUI. This gui will make use of multiple tab's using ttk notebook. However, I'm experiencing a problem regarding to adding multiple listboxes in my programm. I do have a working listbox, in "tab11". But, whenever I try to add a simple listbox it won't render when I run the program. The shell however supplies me with an error, I don't have any clue what the problem is.
The error I get is:
Traceback (most recent call last):
File "/Users/nielsschreuders/Documents/01_TUe_B32_Feb13_Jun13/Project/Concept/prototype/python/RaspberryBackup/B32Project/PythonExperiments/multipleListboxTest.py", line 133, in <module>
listbox2 = tk.Listbox(tab12, width=50, height=-1)
File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/tkinter/__init__.py", line 2608, in __init__
Widget.__init__(self, master, 'listbox', cnf, kw)
File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/tkinter/__init__.py", line 2075, in __init__
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: can't invoke "listbox" command: application has been destroyed
This is a simplefied version of the code:
import tkinter as tk
#import RPi.GPIO as gpio
import sys
import os
from tkinter import ttk
mGui = tk.Tk()
mGui.geometry('800x480')
mGui.title("Apps on Wheels")
nb = ttk.Notebook(mGui, name="notebook")
nb.pack(fill=tk.BOTH, expand=tk.Y, padx=2, pady=3)
dispColor="Yellow"
#create tabs
tab1 = ttk.Frame(nb, name="audioTab")
#assign tabs to notebook children
nb.add(tab1, text="Audio")
nbAS = ttk.Notebook(tab1, name="audioSource")
nbAS.pack(pady=100)
#nbAS.pack(fill=tk.BOTH, expand=tk.Y, padx=2, pady=3)
tab11 = ttk.Frame(nbAS, name="radioTab")
tab12 = ttk.Frame(nbAS, name="mp3Tab")
nbAS.add(tab11, text="Radio")
nbAS.add(tab12, text="MP3")
##################Radio Controller###################
def get_list(event):
#get selected line index
index = listbox1.curselection()[0]
#get the line's text
seltext=listbox1.get(index)
#delete previous text in enter1
enter1.delete(0, 50)
#display selected text
enter1.insert(0, "You are listening to: " +seltext)
intIndex = int(index)
intRadioFreq = intIndex +1
radioFreq = str(intRadioFreq)
os.system("mpc play " + radioFreq)
def set_list(event):
try:
index = listbox1.curselection()[0]
#delete old listbox line
listbox1.delete(index)
except IndexError:
index = tk.END
#insert edited item back into listbox1 at index
listbox1.insert(index, enter1.get())
def sort_list():
temp_list = list(listbox1.get(0, tk.END))
temp_list.sort(key=str.lower)
#delete contents of present listbox
listbox1.delete(0, tk.END)
for item in temp_list:
listbox1.insert(tk.END, item)
def save_list():
#get a list of listbox lines
temp_list = list(listbox1.get(0, tk.END))
#add a trailing newline char to each line
temp_list=[radio + '\n' for radio in temp_list]
#give the file new name
fout = open("radio_data2.txt", "w")
fout.writelines(temp_list)
fout.close()
#create the data file
str1 = """Radio 1
Radio 2
3FM Serious Radio
538 Radio
Radio 6 Soul & Jazz
"""
fout = open("radio_data.txt", "w")
fout.write(str1)
fout.close()
fin = open("radio_data.txt", "r")
radio_list= fin.readlines()
fin.close()
radio_list= [radio.rstrip() for radio in radio_list]
RadioFrequencies = tab11
#creat listbox
listbox1 = tk.Listbox(RadioFrequencies , width=50, height=-1)
listbox1.grid(row=0, column=0)
#use entry widget to display/edit selection
enter1 = tk.Entry(RadioFrequencies , width=44, bg=dispColor, fg="black", justify=tk.CENTER, font=("Arial" ,16, "bold"))
#enter1.insert(0, "Choose your radio station")
enter1.grid(row=1, column=0)
for item in radio_list:
listbox1.insert(tk.END, item)
listbox1.bind("<ButtonRelease-1>", get_list)
RadioFrequencies.mainloop()
listbox2 = tk.Listbox(tab12, width=50, height=-1)
listbox2.place(x=100, y=150)
listbox2.insert(tk.END, "entry here")
for item2 in ["one", "two", "three", "four"]:
listbox2.insert(tk.END, item2)
mGui.mainloop()
Your program calls mainloop() in two places:
RadioFrequencies.mainloop()
...
mGui.mainloop()
Generally speaking, a Tkinter app should call mainloop only once, and it is usually the tk.Tk() instance that makes the call. Once the call is made, the program hands over the flow of control to the Tkinter event loop which manages all the GUI events.
Usually the call to mainloop() would therefore be the last call in the script. When the GUI is exited, the script exits as well.
So remove the line
RadioFrequencies.mainloop()
The reason you are getting the error
_tkinter.TclError: can't invoke "listbox" command: application has been destroyed
is because after you close the GUI, Python returns from
RadioFrequencies.mainloop()
and then tries to call
listbox2 = tk.Listbox(tab12, width=50, height=-1)
but by this point the application has already ended.
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()