Can't overwrite labels Tkinter - python

get_recs is triggered by the start button. A loop in get_recs trys to first delete any existing labels (but always fails), then creates the label, writes to it and then adds it to the grid. However each time start is pressed the existing labels are not destroyed and new labels are created instead of being replaced. I can only assume this means that each time the loop is executed it creates separate labels, which would explain why they are never destroyed but i dont understand why. Here is the code:
import pandas as pd
import numpy as np
from tkinter import *
from tkinter.filedialog import askopenfilename
from tkinter.messagebox import showwarning, showinfo
movies = pd.read_csv('C:/Users/Admin/Python Programs/ml-latest-small/movies.csv')
ratings = pd.read_csv('C:/Users/Admin/Python Programs/ml-latest-small/ratings.csv')
ratings.drop(['timestamp'], axis=1, inplace= True)
class App(Frame):
def replace_name(x):
return movies[movies['movieId']==x].title.values[0]
ratings.movieId = ratings.movieId.map(replace_name)
M = ratings.pivot_table(index=['userId'], columns=['movieId'], values='rating')
def pearsons(s1, s2):
s1_c = s1 - s1.mean()
s2_c = s2 - s2.mean()
return np.sum(s1_c * s2_c) / np.sqrt(np.sum(s1_c ** 2) * np.sum(s2_c ** 2))
def get_recs(self):
movie_name = self.mn.get()
num_recs_str = self.nr.get()
num_recs = int(num_recs_str)
reviews = []
for title in App.M.columns:
if title==movie_name:
continue
cor = App.pearsons(App.M[movie_name], App.M[title])
if np.isnan(cor):
continue
else:
reviews.append((title, cor))
reviews.sort(key=lambda tup: tup[1], reverse=True)
Frame3= Frame(root, bg= 'red')
Frame3.grid()
#for i in range(num_recs):
#exec("Label%d=Label(Frame3,text='', fg = 'blue')\nLabel%d.grid()" % (i,i))
#var_exists = 'Label0' in locals() or 'Label0' in globals()
for i in range(num_recs):
try:
exec("Label%d.destroy()" % (i))
except (NameError):
pass
exec("Label%d=Label(Frame3,text='', fg = 'blue')\n" % (i))
exec("Label%d.config(text=%s)" % (i,reviews[i]))
exec("Label%d.grid()" % (i))
print ("success")
#exec("print (%d)" % (i))
#for x in reviews:
#self.label3.config(text= "\n" + str(reviews[:num_recs]))
#self.label3.config(text=reviews[:num_recs])
return reviews[:num_recs]
def __init__(self, master):
Frame.__init__(self, master)
Frame1 = Frame(master)
Frame1.grid()
Frame2 = Frame(master)
Frame2.grid()
self.filename = None
label1=Label(Frame1, text="Movie: ").grid(row=0, sticky=W)
label2=Label(Frame1, text="Recommendations: ").grid(row=1, sticky=W)
#self.label3=Label(master, text = "", font = 'Purisa', fg='blue')
#self.label3.grid(row = 3)
self.mn = Entry(Frame1)
self.mn.grid(row = 0, column = 1, sticky=W)
#self.mn.delete(0, END)
self.mn.insert(0, "Enter Movie Name")
self.nr = Entry(Frame1)
self.nr.grid(row = 1, column = 1, sticky=W)
#self.nr.delete(0, END)
self.nr.insert(0, "Enter Number of Recommendations")
button1 = Button(Frame2, text="Start", command=self.get_recs)
button2 = Button(Frame2, text="Exit", command=master.destroy)
button1.grid(row = 0, column = 0, padx= 5, pady = 5, sticky =W)
button2.grid(row = 0, column = 1, padx = 5, pady =5, sticky =W)
self.grid()
root = Tk()
root.title("Recommender")
root.geometry("500x500")
app = App(root)
root.mainloop()

Your code throws a NameError because your labels are created locally. On your second click you can't reach them and since you are just passing in except block, you don't see anything.
One approach is, you can create labels as class variables
for i in range(num_recs):
try:
exec("self.Label%d.destroy()" % (i))
except NameError:
print("A NameError has occured")
except AttributeError:
exec("self.Label%d=Label(Frame3,text='', fg = 'blue')\n" % (i))
exec("self.Label%d.config(text=%s)" % (i,reviews[i]))
exec("self.Label%d.grid()" % (i))
Instead of this approach, you can put all your labels into a list then check if said label is in list or not. Which is, IMHO, much better approach.
#a list created in global scope to store all labels
labels = []
def get_recs(self):
....
for i in range(num_recs):
try:
for label in labels:
if label["text"] == reviews[i]:
label.destroy() #destroy the matching label
labels.remove(label) #remove it from labels list
except (NameError):
#Please don't just pass Exceptions. They are there for a reason
print("A NameError has occured")
labels.append(Label(Frame3,text='', fg = 'blue'))
labels[-1].config(text=reviews[i])
labels[-1].grid()

Related

Python Tkinter Spinboxes return value using for loops

I'm trying to use X many spin boxes as there are items in a list - so have put them on screen using a for loop.
However, I don't seem able to get them to return their value when I click a button.
aStarter = ["Starter 1", "Starter 2", "Starter 3"]
def getOrders():
aStarterOrder = []
aMainOrder = []
aDessertOrder = []
for i in aStarterQuantity:
aStarterOrder.append(StarterQuantity.get())
print(aStarterOrder)
aStarterQuantity = []
StarterQuantity = IntVar()
for i in range(len(aStarter)):
lbl = Label(self, text = aStarter[i]).grid(column = 0, row = 2 + i)
StarterQuantity = Spinbox(self, from_=0, to=20, width=5, command=callback, font=Font(family='Helvetica', size=20, weight='bold')).grid(column = 1, row = 2 + i)
print(StarterQuantity.get())
I have tried appending the StarterQuantity to an array inside of the for loop:
aStarterQuantity = []
StarterQuantity = IntVar()
for i in range(len(aStarter)):
lbl = Label(self, text = aStarter[i]).grid(column = 0, row = 2 + i)
StarterQuantity = Spinbox(self, from_=0, to=20, width=5, command=callback, font=Font(family='Helvetica', size=20, weight='bold')).grid(column = 1, row = 2 + i)
aStarterQuantity.append(StarterQuantity.get())
This returns a None Type error - I can't seem to get the value out of the spin box.
Any ideas?
Here is the full code (it's very much an early WIP!)
from tkinter import *
from tkinter.font import Font
import sqlite3
DatabaseFile = 'RMS.db'
connDatabase = sqlite3.connect(DatabaseFile)
#Creating a cursor to run SQL Commands
DatabaseSelect = connDatabase.cursor()
#Selecting all Menu Data
DatabaseSelect.execute("SELECT * FROM MENU")
MenuData = DatabaseSelect.fetchall()
class Main(Tk): #This sets up the initial Frame in a Window called Main
def __init__(self): #This creates a controller to use the Frames
Tk.__init__(self) #This creates a Window within the controller
self._frame = None #This sets the frame to have a value of None - this makes sure the main Window doesn't get destroyed by the switch frame function
self.switch_frame(Home)
def switch_frame(self, frame_class): #This function is used to switch frames. Self is the master frame, frame_class is the name of the Class (page) being passed int
new_frame = frame_class(self)
if self._frame is not None:
self._frame.destroy() #Destroy any frames except the master
self._frame = new_frame #Make a new frame
self._frame.grid(column=0, row=0) #Put the frame in the top left
class Home(Frame):
def __init__(self, master):
Frame.__init__(self, master)
Label(self, text="The White Horse").grid(column = 0, row = 0)
btnPage1 = Button(self, text="Orders", command=lambda: master.switch_frame(Orders)).grid(column = 1, row = 0)
#Making a blank page with a return home button
class Orders(Frame): #This sets up a class called Page1 - Each new page will need a new class.
def __init__(self, master): #This sets up a controller to put things into the Frame
Frame.__init__(self, master) #Make the frame using the controller
#Get list of Starters from Menu Data
aStarter = ["Starter 1", "Starter 2", "Starter 3"]
aMain = []
aDessert = []
for i in MenuData:
if i[3] == "Starters":
aStarter.append(i[1])
if i[3] == "Main":
aMain.append(i[1])
if i[3] == "Dessert":
aDessert.append(i[1])
def getOrders():
aStarterOrder = []
aMainOrder = []
aDessertOrder = []
for i in aStarterQuantity:
aStarterOrder.append(StarterQuantity.get())
print(aStarterOrder)
def callback():
print(StarterQuantity.get())
#Starter Section
boxes = []
aStarterQuantity = []
StarterQuantity = IntVar()
for i in range(len(aStarter)):
lbl = Label(self, text = aStarter[i]).grid(column = 0, row = 2 + i)
StarterQuantity = Spinbox(self, from_=0, to=20, width=5, command=callback, font=Font(family='Helvetica', size=20, weight='bold')).grid(column = 1, row = 2 + i)
aStarterQuantity.append(StarterQuantity.get())
#Mains Sections
for i in range(len(aMain)):
lbl = Label(self, text = aMain[i]).grid(column = 6, row = 2 + i)
MainQuantity = Spinbox(self, from_=0, to=20, width=5, font=Font(family='Helvetica', size=20, weight='bold')).grid(column = 7, row = 2 + i)
#Dessert Section
for i in range(len(aDessert)):
lbl = Label(self, text = aDessert[i]).grid(column = 12, row = 2 + i)
DessertQuantity = Spinbox(self, from_=0, to=20, width=5, font=Font(family='Helvetica', size=20, weight='bold')).grid(column = 13, row = 2 + i)
btnHome = Button(self, text="Home", command=lambda: master.switch_frame(Home)).grid(column = 0, row = 1) #Add a Button
btnSubmitEntries = Button(self, text = "Submit", command = getOrders).grid(column = 0, row= 20)
Main = Main()
Main.geometry("1200x800")
Main.mainloop()
The problem seems to be that the grid method doesn't return anything, so StarterQuantity gets assigned None i.e. the default return value for a function.
for i in range(len(aStarter)):
lbl = Label(self, text=aStarter[i])
lbl.grid(column=0, row=2 + i)
sq_font = Font(family='Helvetica',
size=20,
weight='bold')
StarterQuantity = Spinbox(self,
from_=0,
to=20,
width=5, command=callback,
font=sq_font)
StarterQuantity.grid(column=1, row=2 + i)
aStarterQuantity.append(StarterQuantity.get())
This works in my case. (Pulled out the Font argument for Spinbox to increase readability on mobile).
Unrelated note; I don't know what code style you prefer/have to maintain and it is up to you of course, but I thought it might be helpful to mention that the naming style convention in Python (PEP8) is to use CamelCase for class names, lower_case_w_underscores for variable names and functions/methods, and SCREAMING_SNAKE for constants.

Is it possible to grab a label variable and put into list with tkinter

I've created a temperature converter programme in which the calculated temperature from an entry widget gets displayed in a separate label, what I need to do is to grab that converted variable and put it into a list.
I think that making a connected entry widget to the label widget would work where they are connected so I could grab the variable using the .get method but that would look awfully messy. Is there any other way I could proceed with this?
This is my first post and I am a beginner in Python, very sorry if the code looks messy and if I included too much code.
data = []
tempVal = "Celcius"
def store_temp(sel_temp):
global tempVal
tempVal = sel_temp
class Calculator:
def __init__(self, num_a, num_b):
self.num_a= num_a
self.num_b = num_b
def convert(self):
if tempVal == 'Fahrenheit':
return float((float(self.num_a) - 32)* 5 / 9)
if tempVal == 'Celcius':
return float((float(self.num_a) * 9/ 5) + 32)
def display_add(entry_numa,entry_numb,label_answer):
#get the value from entry_numa
num_a = entry_numa.get()
num_b = entry_numb.get()
num_a = str(num_a)
num_b = str(num_b)
#create an object
global data
calc = Calculator(num_a,num_b)
label_answer['text'] = calc.convert()
data += [calc]
def calc_history():
global data
#creat e another window
window_calc_list = Tk()
window_calc_list.geometry("400x200")
#create a listbox
listbox_calc_list = Listbox(window_calc_list, width= 300)
listbox_calc_list.pack()
listbox_calc_list.insert(END, "list of data")
for info in data:
listbox_calc_list.insert(END, str(info.num_a) + " " + str(info.num_b) + " " )
window_calc_list.mainloop()
def main():
window = Tk()
window.geometry("500x150")
validate_letter = window.register(only_letters)
validate_nb = window.register(only_numbers_max_3)
label = Label(window, width = 30, background = 'lightblue', text='enter temperature, only numbers')
label.grid(row=0, column=0)
entry_numa = Entry(window, width = 30, validate="key", validatecommand=(validate_nb, '%d', '%P'))
entry_numa.grid(row = 0, column = 1)
#create another label and entry object for num_b
label_numb = Label(window, width = 30, background = 'lightblue', text='enter location, only letters')
label_numb.grid(row=1, column=0)
entry_numb = Entry(window, width = 30, validate="key", validatecommand=(validate_letter, '%d', '%S'))
entry_numb.grid(row = 1, column = 1)
#create another label to display answer
label_answer = Label(window, width = 30, background = 'lightyellow')
label_answer.grid(row = 2, column = 1)
entry_answer = Entry(window, width = 30)
entry_answer.grid(row = 2, column = 0)
button_add = Button(window, text = "ADD", command = lambda: display_add(entry_numa,entry_numb,label_answer))
button_add.grid(row=3, column = 0)
button_delete = Button(window, text = "DELETE", command = lambda: delete_data(data))
button_delete.grid(row=3, column = 2)
#create another button to display all previous calculations
button_display = Button(window,text = "calc_history", command = lambda: calc_history())
button_display.grid(row=3, column = 1)
var = StringVar()
dropDownList = ["Celcius", "Fahrenheit"]
dropdown = OptionMenu(window, var,dropDownList[0], *dropDownList, command=store_temp)
dropdown.grid(row=0, column=2)
window.mainloop()
A tk.Label displayed value can be accessed via the text property
, labelwidgetname['text'].
Depeneding on when and how you want the independent list of stored values
to be updated there are a variety of options. The example shows one if the
user is required to press a submission button. This could be adapted,
for example,when the tempreture calculation is performed.
Of course it would be simpler to update the list of stored values directly at the point in the script where the calculated tempreture for the label text has been derived.
import tkinter as tk
stored_values = []
def add_labelvalue_tolist(temp):
'''Store label value to list.'''
stored_values.append(temp)
print('contents of list', stored_values)
def add_entry_tolabel(event):
display_label['text'] = user_entry.get()
ROOT = tk.Tk()
user_entry = tk.Entry()
user_entry.grid(column=0, row=0)
user_entry.bind('<KeyRelease>', add_entry_tolabel)
display_label = tk.Label()
display_label.grid(column=1, row=0)
# update list via button command linked to label text value
add_button = \
tk.Button(text='add to list',
command=lambda:add_labelvalue_tolist(display_label['text']))
add_button.grid(column=0, row=1)
ROOT.mainloop()
try making a function that is like this
def letterused():
converter=(letter.get())# letter is a entry box at the bottom is the code
converted.set(converter)
for i in range(1):
used_letters1.append(converter) #list
letter = ttk.Entry(root, width = 20,textvariable = letter)
letter.pack()

Printing list to Tkinter Label

Trying to print the reviews array as a list to my label so it doesn't just come out in a single line.
import pandas as pd
import numpy as np
from tkinter import *
from tkinter.filedialog import askopenfilename
from tkinter.messagebox import showwarning, showinfo
movies = pd.read_csv('C:/Users/Admin/Python Programs/ml-latest-small/movies.csv')
ratings = pd.read_csv('C:/Users/Admin/Python Programs/ml-latest-small/ratings.csv')
ratings.drop(['timestamp'], axis=1, inplace= True)
class App(Frame):
def replace_name(x):
return movies[movies['movieId']==x].title.values[0]
ratings.movieId = ratings.movieId.map(replace_name)
M = ratings.pivot_table(index=['userId'], columns=['movieId'], values='rating')
def pearsons(s1, s2):
s1_c = s1 - s1.mean()
s2_c = s2 - s2.mean()
return np.sum(s1_c * s2_c) / np.sqrt(np.sum(s1_c ** 2) * np.sum(s2_c ** 2))
def get_recs(self):
movie_name = self.mn.get()
num_recs_str = self.nr.get()
num_recs = int(num_recs_str)
reviews = []
for title in App.M.columns:
if title==movie_name:
continue
cor = App.pearsons(App.M[movie_name], App.M[title])
if np.isnan(cor):
continue
else:
reviews.append((title, cor))
reviews.sort(key=lambda tup: tup[1], reverse=True)
for x in reviews:
self.label3.config(text= "\n" + str(reviews[:num_recs]))
#self.label3.config(text=reviews[:num_recs])
return reviews[:num_recs]
def __init__(self, master):
Frame.__init__(self, master)
self.filename = None
label1=Label(master, text="Movie: ").grid(row=0)
label2=Label(master, text="Recommendations: ").grid(row=1)
self.label3=Label(master, text = "", font = 'Purisa', fg='blue')
self.label3.grid(row = 3)
self.mn = Entry(master)
self.mn.grid(row = 0, column = 1)
#self.mn.delete(0, END)
self.mn.insert(0, "Enter Movie Name")
self.nr = Entry(master)
self.nr.grid(row = 1, column = 1)
#self.nr.delete(0, END)
self.nr.insert(0, "Enter Number of Recommendations")
button1 = Button(self, text="Start", command=self.get_recs)
button2 = Button(self, text="Exit", command=master.destroy)
button1.grid(row = 2, padx= 5)
button2.grid(row = 2, column = 1, padx = 5)
self.grid()
root = Tk()
root.title("Recommender")
root.geometry("500x500")
app = App(root)
root.mainloop()
Here is an image of the current output
I couldn't modify your code because I don't have the files you opened in your code. However, this is an idea to solve your question:
from tkinter import *
things_to_print=['something','one thing','another']
root=Tk()
for i in range(len(things_to_print)):
exec('Label%d=Label(root,text="%s")\nLabel%d.pack()' % (i,things_to_print[i],i))
root.mainloop()
I think you can solve your problem by substituting your reviews into my things_to_print and maybe some other slight modifications.

get previous Entry

I am trying to make a tkinter widget that will remember the previous entry. The problem I am having is the that the button method I made erases the previous entry every time its pressed.
from Tkinter import *
class step1():
def __init__(self):
pass
def getTextbook(self):
temp = str(textbook.get())
textbook.delete(0, END)
x = " "
x += temp
print x
def equal_button(self):
print getTextbook(self)
root = Tk()
root.title("step1")
root.geometry("600x600")
s = step1()
app = Frame(root)
app.grid()
label = Label(app, text = "step1")
label.grid()
textbook = Entry(app, justify=RIGHT)
textbook.grid(row=0, column = 0, columnspan = 3, pady = 5)
textbook2 = Entry(app, justify=RIGHT)
textbook2.grid(row=1, column = 0, columnspan = 3, pady = 5)
button1 = Button(app, text = "Return", command = lambda: s.getTextbook())
button1.grid()
button2 = Button(app, text="Equal")
button2.grid()
root.mainloop()
The variable X in your getTextbook() is being overwritten every time you set it to " ". Try a list instead, and append each entry to the list when the button is pressed:
from Tkinter import *
root = Tk()
textbookList = []
def getTextbook():
textbookList.append(textbook.get())
textbook.delete(0,END)
print textbookList
textbook = Entry(root)
textbook.pack()
btn1 = Button(root, text='Return', command=getTextbook)
btn1.pack()
root.mainloop()

How can I make a in interactive list in Python's Tkinter complete with buttons that can edit those listings?

Basically I want there to be a list, which displays files I have stored in a certain folder, and beside the list there are buttons which open up separate windows which can edit or add a new item to that list.
I want addchar to open up an new window with spaces for different fields, then when you press a "create" button in that window it closes, creates a file on the info you just entered (that's why i've imported os) as well as creating a new item on the main interface's listbox as the name of the character (which is one of the fields). The removechar function would remove that entry and delete the file, and editchar would open up a window similar to addchar, but with the info of the selected item on the list.
EDIT: Here's the code so far
from tkinter import *
import os
import easygui as eg
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
# character box
Label(frame, text = "Characters Editor").grid(row = 0, column = 0, rowspan = 1, columnspan = 2)
charbox = Listbox(frame)
for chars in []:
charbox.insert(END, chars)
charbox.grid(row = 1, column = 0, rowspan = 5)
charadd = Button(frame, text = " Add ", command = self.addchar).grid(row = 1, column = 1)
charremove = Button(frame, text = "Remove", command = self.removechar).grid(row = 2, column = 1)
charedit = Button(frame, text = " Edit ", command = self.editchar).grid(row = 3, column = 1)
def addchar(self):
print("not implemented yet")
def removechar(self):
print("not implemented yet")
def editchar(self):
print("not implemented yet")
root = Tk()
root.wm_title("IA Development Kit")
app = App(root)
root.mainloop()
Ok, I implemented addchar and removechar for you (and tweaked a few other things in your code). I'll leave the implementation of editchar to you - take a look at what I wrote to help you, as well as the listbox documentation
from Tkinter import * # AFAIK Tkinter is always capitalized
#import os
#import easygui as eg
class App:
characterPrefix = "character_"
def __init__(self, master):
self.master = master # You'll want to keep a reference to your root window
frame = Frame(master)
frame.pack()
# character box
Label(frame, text = "Characters Editor").grid(row = 0, column = 0, rowspan = 1, columnspan = 2)
self.charbox = Listbox(frame) # You'll want to keep this reference as an attribute of the class too.
for chars in []:
self.charbox.insert(END, chars)
self.charbox.grid(row = 1, column = 0, rowspan = 5)
charadd = Button(frame, text = " Add ", command = self.addchar).grid(row = 1, column = 1)
charremove = Button(frame, text = "Remove", command = self.removechar).grid(row = 2, column = 1)
charedit = Button(frame, text = " Edit ", command = self.editchar).grid(row = 3, column = 1)
def addchar(self, initialCharacter='', initialInfo=''):
t = Toplevel(root) # Creates a new window
t.title("Add character")
characterLabel = Label(t, text="Character name:")
characterEntry = Entry(t)
characterEntry.insert(0, initialCharacter)
infoLabel = Label(t, text="Info:")
infoEntry = Entry(t)
infoEntry.insert(0, initialInfo)
def create():
characterName = characterEntry.get()
self.charbox.insert(END, characterName)
with open(app.characterPrefix + characterName, 'w') as f:
f.write(infoEntry.get())
t.destroy()
createButton = Button(t, text="Create", command=create)
cancelButton = Button(t, text="Cancel", command=t.destroy)
characterLabel.grid(row=0, column=0)
infoLabel.grid(row=0, column=1)
characterEntry.grid(row=1, column=0)
infoEntry.grid(row=1, column=1)
createButton.grid(row=2, column=0)
cancelButton.grid(row=2, column=1)
def removechar(self):
for index in self.charbox.curselection():
item = self.charbox.get(int(index))
self.charbox.delete(int(index))
try:
os.remove(characterPrefix + item)
except IOError:
print "Could not delete file", characterPrefix + item
def editchar(self):
# You can implement this one ;)
root = Tk()
root.wm_title("IA Development Kit")
app = App(root)
root.mainloop()

Categories

Resources