Linked data from database to a button tkinter - python

I am working on a project on tkinter python.
This is how my graphic interface looks like:
And I have this database.txt:
ChickenCurry,Rice,Curry,Chicken,0.ppm
ChocolateCake,Chocolate,Flour,Sugar,Eggs,1.ppm
BolognesePasta,Pasta,Beef,TomatoeSauce,Cheese,2.ppm
Really simple. 0.ppm, 1.ppm and 2.ppm are the name of 3 images, the first one is the image of chicken curry the second one of chocolate and the last one of bolognese pasta.
My project: I would like to display the image of the chicken dish when I am clicking on the button ChickenCurry, the image of the chocolate cake when I am clicking on the chocolate cake, etc...
Here is my code:
import sys
from tkinter import *
import tkinter as tk
from PIL import Image
class Application(tk.Frame):
x = 2
def __init__(self, param = None, i = None, master=None):
super().__init__(master)
self.master = master
self.pack()
self.create_widgets()
def create_widgets(self):
if (self.x == 2):
param = "coucou"
self.hi_there = tk.Label(self)
self.hi_there["text"] = param
#self.hi_there["command"] = self.say_hi
self.hi_there.pack(side="top")
self.quit = tk.Button(self, text="QUIT", fg="red",
command=self.master.destroy)
self.quit.pack(side="bottom")
# Opening file in read format
File = open('data.txt',"r")
if(File == None):
print("File Not Found..")
else:
while(True):
# extracting data from records
record = File.readline()
if (record == ''): break
data = record.split(',')
print('Name of the dish:', data[0])
self.hi_there = tk.Button(self)
self.hi_there["text"] = data[0]
self.hi_there["command"] = self.photoOfTheDish
self.hi_there.pack(side="top")
# printing each record's data in organised form
for i in range(1, len(data)-1):
print('Ingredients:',data[i])
self.hi_there = tk.Label(self)
self.hi_there["text"] = data[i]
self.hi_there.pack(side="top")
File.close()
def photoOfTheDish(self):
novi = Toplevel()
self.canvas = Canvas(novi, width = 1500, height = 1000)
self.canvas.pack(expand = YES, fill = BOTH)
File = open('data.txt',"r")
with open('data.txt') as f:
record = File.readline()
data = record.split(',')
gif1 = PhotoImage(file = data[-1].rstrip('\n'))
#image not visual
self.canvas.create_image(50, 10, image = gif1, anchor = NW)
#assigned the gif1 to the canvas object
self.canvas.gif1 = gif1
root = tk.Tk()
root.geometry("5000x2000")
app = Application(master=root)
app.mainloop()
My issue is whatever the button I am clicking on, it's always the image corresponding to "0.ppm" which is displaying. I don't know how to link the button to his set of value from the database.

Inside photoOfTheDish(), you open data.txt and read only the first line to get the image filename. Therefore you always get the 0.ppm.
You can use lambda to pass the image filename to photoOfTheDish() when creating the buttons:
def create_widgets(self):
...
else:
while True:
...
# extracting data from records
record = File.readline().rstrip() # strip out the trailing newline
...
# pass image filename to callback
self.hi_there["command"] = lambda image=data[-1]: self.photoOfTheDish(image)
...
...
def photoOfTheDish(self, image):
novi = Toplevel()
self.canvas = Canvas(novi, width = 1500, height = 1000)
self.canvas.pack(expand = YES, fill = BOTH)
self.canvas.gif1 = PhotoImage(file=image)
self.canvas.create_image(50, 10, image=self.canvas.gif1, anchor=NW)

Related

How can I detect which on which frame was a Button Clicked in Tkinter?

I have a question. I have this code:
import tkinter as tk
class new_f:
def __init__(self,root,num):
self.new_frame=tk.Frame(root,width=100,height=100,bg='white',bd=3,relief=tk.GROOVE)
self.new_frame.pack(side=tk.LEFT,fill=tk.X,expand=True)
self.num=num
def add_label(self,t):
self.l1=tk.Label(self.new_frame,bg='white',text=t)
self.l1.pack()
def return_instance(self):
return self.num
class Main_win:
def __init__(self,root):
self.root=root
self.bind_number=0
self.current_index=0
self.instance_list=[]
self.b1=tk.Button(self.root,text='Add Frame',command=self.add_frame_win)
self.b1.pack(side=tk.BOTTOM)
self.b2=tk.Button(self.root,text='Add text',command=self.add_text_frame)
self.b2.pack(side=tk.BOTTOM)
def return_instance_num(self,num,*args):
self.current_index=num
def add_frame_win(self):
new_in=new_f(self.root,self.bind_number)
self.instance_list.append(new_in)
new_in.new_frame.bind('<Button-1>',lambda evnt: self.return_instance_num(new_in.return_instance()))
#self.current_index=new_in.return_instance()
self.bind_number+=1
def add_text_frame(self):
instance=self.instance_list[self.current_index]
instance.add_label('Hello World')
root=tk.Tk()
ob=Main_win(root)
root.mainloop()
What I a trying to achieve is that I want to detect on which frame was the left mouse-button clicked so as to make that Frame active and add the labels to that particular Frame. However, I am stuck on how would I go about writing the code. I need a new class Because I don't know how many frames will the user need.
This is a short example of the code I will be implementing later. So my question is:
How will I go to detect which frame was picked so as to make it active to add the labels?
In this approach I have label l1 bound to Button-1
This was achieved by passing self to new_f instead of root
and binding self.l1 to Button-1
import tkinter as tk
class new_f:
def __init__(self, prog, num):
self.prog = prog
self.new_frame = tk.Frame(prog.root, width = 100, height = 100, bg = 'white', bd = 3, relief = tk.GROOVE)
self.new_frame.pack(side = tk.LEFT, fill = tk.X, expand = True)
self.num = num
def add_label(self, t):
self.l1 = tk.Label(self.new_frame, bg = 'white', text = t)
self.l1.pack()
# binding button-1 press to label
self.l1.bind("<Button-1>", lambda evnt: self.prog.return_instance_num(self.return_instance()))
def return_instance(self):
return self.num
class Main_win:
def __init__(self, root):
self.root = root
self.bind_number = 0
self.current_index = 0
self.instance_list = []
self.b1 = tk.Button(self.root, text = 'Add Frame', command = self.add_frame_win)
self.b1.pack(side = tk.BOTTOM)
self.b2 = tk.Button(self.root, text = 'Add text', command = self.add_text_frame)
self.b2.pack(side = tk.BOTTOM)
def return_instance_num(self, num, *args):
self.current_index = num
def add_frame_win(self):
# note passing self not root
new_in = new_f(self, self.bind_number)
self.instance_list.append(new_in)
new_in.new_frame.bind('<Button-1>', lambda evnt: self.return_instance_num(new_in.return_instance()))
#self.current_index = new_in.return_instance()
self.bind_number = self.bind_number + 1
def add_text_frame(self):
instance = self.instance_list[self.current_index]
instance.add_label('Hello World')
root = tk.Tk()
ob = Main_win(root)
# This necessary to prevent error if user hits 'Add text' before 'Add Frame'
ob.add_frame_win()
root.mainloop()
Here is an alternative method that uses dictionaries to store l1 and new_frame objects as keys and new_f instances as values.
This method can be used for other tkinter objects (Entry, Listbox, Text, Canvas)
import tkinter as tk
class new_f:
def __init__(self, parent):
self.parent = parent
self.frame = tk.Frame(
parent.root, width = 100, height = 100,
bg = "white", bd = 3, relief = tk.GROOVE)
self.frame.pack(
side = tk.LEFT, fill = tk.X, expand = True)
self.frame.bind("<Button-1>", parent.get_current_frame)
def add_label(self, t):
self.label = tk.Label(self.frame, bg = "white", text = t)
self.label.pack(fill = tk.BOTH, expand = True)
# bind button-1 to label, set instance_label and current to self
self.label.bind("<Button-1>", self.parent.get_current_label)
self.parent.instance_label[self.label] = self.parent.current = self
class Main_win:
instance_label = dict() # This method can be expanded for other objects
instance_frame = dict() # that you may want to create in frames
def __init__(self, root):
self.root = root
self.b1 = tk.Button(
self.root, text = "Add Frame", command = self.add_frame_win)
self.b1.pack(side = tk.BOTTOM)
self.b2 = tk.Button(
self.root, text = "Add text", command = self.add_text_frame)
self.b2.pack(side = tk.BOTTOM)
def get_current_label(self, ev):
self.current = self.instance_label[ev.widget]
def get_current_frame(self, ev):
self.current = self.instance_frame[ev.widget]
def add_frame_win(self):
# note passing self not root
self.new_in = new_f(self)
self.instance_frame[self.new_in.frame] = self.current = self.new_in
def add_text_frame(self):
# Change message with entry tool?
self.current.add_label("Hello World")
root = tk.Tk()
ob = Main_win(root)
# This necessary to prevent error if user hits 'Add text' before 'Add Frame'
ob.add_frame_win()
root.mainloop()

is there a way to grid an extra button while running your program in tkinter correctly?

I'm making an mp3 player app and I want the user to be able to add playlists (which will be buttons in a grid system.
Like so.
However in this state when I'm trying to add a playlist button this happens:
The top buttons and the button i just added get squeezed off and the scrollbar is stuck (I can't use it)
I've tried refreshing by self.playlist_frame.update_idletasks() after running create_playlists() but it doesn't seem to change anything
this is my code so you can test it:
import os
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
class MyApp:
def __init__(self):
self.root = tk.Tk()
self.width = self.root.winfo_screenwidth()
self.height = self.root.winfo_screenheight()
self.height = self.root.winfo_screenheight() - int(self.height / 13)
self.root.geometry(f'{self.width}x{self.height}')
self.root.title("Mp3")
self.c = '#14141D'
self.playlists_buttons_list = []
self.playlists = {}
self.helping_d = {}
self.main = ttk.Notebook(self.root)
self.playlists_tab = tk.Frame(self.main)
self.playlist_menu_frame = tk.Frame(self.playlists_tab, bg=self.c)
self.playlists_canvas = tk.Canvas(self.playlists_tab, bg=self.c)
self.playlist_frame = tk.Frame(self.playlists_canvas, bg=self.c)
self.playlists_scrollbar = ttk.Scrollbar(self.playlists_tab, orient='vertical',
command=self.playlists_canvas.yview)
self.add_playlist_win = tk.Toplevel(self.root)
self.add_playlists_button = tk.Button(self.playlist_frame)
self.default_img = Image.open('images/default.png').resize((75, 72))
self.default_img = ImageTk.PhotoImage(self.default_img)
self.add_img = Image.open('images/add.png').resize((50, 50))
self.add_img = ImageTk.PhotoImage(self.add_img)
self.widgets()
self.root.mainloop()
def widgets(self):
self.playlists_tab.place(height=self.height, width=self.width, x=0, y=0)
self.main.add(self.playlists_tab, text="Playlists")
self.main.pack(expand=1, fill="both")
self.playlist_frame = tk.Frame(self.playlists_canvas, bg=self.c)
self.playlists_canvas.create_window((0, 0), window=self.playlist_frame, anchor='center')
self.playlists_canvas.config(bg=self.c)
self.playlists_canvas.place(height=int(self.height / 1.428), width=self.width, x=0, y=int(self.height / 9))
self.add_playlists_button = tk.Button(self.playlists_canvas, image=self.add_img, bg=self.c, bd=0,
command=self.add_playlists)
self.add_playlists_button.place(x=1284, y=75)
self.playlists_scrollbar.pack(side='right', fill='y')
self.playlists_canvas.config(yscrollcommand=self.playlists_scrollbar.set)
self.playlists_canvas.bind('<Configure>', lambda e: self.playlists_canvas.configure(
scrollregion=self.playlists_canvas.bbox('all')))
if os.path.getsize('playlistsnames.txt') != 0:
lines = open('playlistsnames.txt', 'r', encoding='utf-8').read().splitlines()
for i in lines:
self.playlists[i] = []
self.create_playlists()
def create_playlists(self):
self.playlists_buttons_list = []
for i in range(len(self.playlists.keys())):
self.playlists_buttons_list.append(tk.Button(self.playlist_frame, bg=self.c,
text=' ' + list(self.playlists.keys())[i].split('!')[0],
fg='white', image=self.default_img, height=280, width=300))
row = 0
column = 0
for i in self.playlists_buttons_list:
if column > 3:
row += 1
column = 0
if column > 7:
row += 1
column = 0
i.grid(row=row, column=column)
column += 1
def get_name(self):
self.name_of_plst = self.plst_entry.get()
self.playlists[self.name_of_plst] = []
self.create_playlists()
with open('playlistsnames.txt', 'a', encoding='utf-8') as names_file:
names_file.write(self.name_of_plst + '!\n')
def add_playlists(self):
self.add_playlist_win = tk.Toplevel(self.root)
self.name_label = tk.Label(self.add_playlist_win, text='Name:', bg=self.c, fg='pink').pack()
self.plst_entry = tk.Entry(self.add_playlist_win)
self.plst_entry.pack()
self.confirm_btn = tk.Button(self.add_playlist_win, text='Confirm', bg=self.c, fg='pink',
command=self.get_name).pack()
self.add_playlist_win.mainloop()
MyApp()
I should also mention that because I store the playlist names in a file called playlistnames.txt when I rerun the program I can see all the playlist buttons plus the one I created before and things work just fine.
So what I want is making the grid update and work while running the app without having to rerun the app of course
My playlistsnames.txt file has this format:
rock!
metal!
chill!
rap!
jazz!
blues!
hard rock!
trap!
Any suggestions would be helpful!
Thanks in advance!

Running into problems with class inheritance an tkinter listBox insert

I am currently trying to make a simple Jukebox/Music Player in Python and ran into some concerns with class inheritance and tkinter's listBox method.
I have to two classes JukeboxGUI and JukeboxContent (the content one is incomplete). The JukeboxContent class inherits the JukeboxGUI:
import os
import pygame
#from PIL import Image
from tkinter.filedialog import askdirectory
from tkinter import *
import eyed3
class JukeboxGUI:
def __init__(self, window):
self.photo = PhotoImage(file = "//Users//nukhbahmajid//Desktop//Jukebox//background2.gif")
self.canvas = Canvas(width = 1000, height = 1000, bg = "black")
self.label = Label(self.canvas, text = "Jukebox")
self.listBox = Listbox(self.canvas)
self.playPauseButton = Button(self.canvas, text = "Play / Pause")
self.nextButton = Button(self.canvas, text = "Next Song")
self.previousButton = Button(self.canvas, text = "Previous Song")
self.stopButton = Button(self.canvas, text = "Stop")
self.labelVar = StringVar()
self.songLabel = Label(self.canvas, textvariable = self.labelVar, width = 20)
def constructButtons(self):
self.canvas.pack()
self.canvas.create_image(0,0, image = self.photo, anchor = NW)
self.label = self.canvas.create_window(325, 40, anchor = NW, window = self.label)
self.listBox = self.canvas.create_window(270, 80, anchor = NW, window = self.listBox)
self.playPauseButton = self.canvas.create_window(110, 120, anchor = NW, window = self.playPauseButton)
self.nextButton = self.canvas.create_window(500, 120, anchor = NW, window = self.nextButton)
self.previousButton = self.canvas.create_window(500, 180, anchor = NW, window = self.previousButton)
self.stopButton = self.canvas.create_window(130, 183, anchor = NW, window = self.stopButton)
self.songLabel = self.canvas.create_window(268, 268, anchor = NW, window = self.songLabel)
class JukeboxContent(JukeboxGUI):
def __init__(self, window):
#JukeboxGUI.__init__(self, window)
super(JukeboxContent, self).__init__(window)
listOfSongs = []
songTitles = []
num_songs = len(songTitles)
self.index = 1
self.listOfSongs = listOfSongs
self.songTitles = songTitles
self.directoryAsk = askdirectory()
self.num_songs = num_songs
self.Error_NoMP3s = "No \".mp3\" files found."
def directoryChooser(self):
self.directoryAsk
os.chdir(self.directoryAsk)
for files in os.listdir(self.directoryAsk):
if files.endswith(".mp3"):
realdir = os.path.realpath(files)
audioTag = eyed3.load(realdir)
self.songTitles.append(audioTag.tag.title)
self.listOfSongs.append(files)
print(files) ## comment out or remove later
print("These are the Song titles:", self.songTitles) #check if the list gets appended
for items in self.songTitles:
self.listBox.insert(END, items) ## the list doesn't get inserted into the listbox
pygame.mixer.init()
pygame.mixer.music.load(self.listOfSongs[0])
self.labelVar.set(self.songTitles[0])
print("The label variable:", self.labelVar) ## the variable isn't set either
print("The label is accessible:", self.songLabel) ## songLabel doesn't get updated
pygame.mixer.music.play() # the song doesn't play
if __name__ == "__main__":
window = Tk()
window.geometry("700x500+450+200")
window.title("Jukebox")
constructGUI = JukeboxGUI(window)
constructGUI.constructButtons()
initiateJukebox = JukeboxContent(window)
initiateJukebox.directoryChooser()
window.mainloop()
When I try to access the attributes of the parent class, like inserting the songs into the list box, there isn't an error here specifically, but the list isn't into the box either
Secondly, when I update the label variable and songLabel, and try to print out later to see if it was implemented, the following gets printed to the terminal:
The label variable: PY_VAR1
The label is accessible: .!canvas2.!label2
The song doesn't play either. What could be the problem? Please help me out! Here's a picture of the interface if it helps:
JukeboxInterface

Duplicating values in text file

I am using a text file to store values for a tkinter combobox. If the user enters a value not in the file, I want it added. Everything works fine, but if the user selects an existing value, it also is added again to the list. I believe it is because I am returning stripped values and comparing against values with '\n'. Any help on how to correct this is greatly appreciated.
from tkinter import *
from tkinter import ttk
regionList = open('regions1.txt','r')
root = Tk()
root.configure()
varRegions = StringVar(root, value='')
class MainWindow(Frame):
def __init__(self,master = None):
Frame.__init__(self,master)
self.master = master
self.grid()
self.create_widgets()
def create_widgets(self):
"""Create Window Layout"""
Boxfont = ('Arial', 12, 'bold')
self.blank = Label(self,text='').grid(row=2,column=0)
self.label = Label(self, font=Boxfont, text="Regions").grid(row=3,column=1)
self.regcombo = ttk.Combobox(self, font = Boxfont, width = 16, textvariable = varRegions)
self.regcombo.bind("<Return>", self.regcombo_onEnter)
self.regcombo.bind('<<ComboboxSelected>>',self.regcombo_onEnter)
self.regcombo['values'] = regionList.readlines()
self.regcombo.grid(row=3, column=2,sticky = W)
self.blank = Label(self,text='').grid(row=4,column=0)
def regcombo_onEnter(self,event):
varRegions.set(varRegions.get().lower().strip())
mytext = varRegions.get().strip()
vals = self.regcombo.cget('values')
self.regcombo.select_range(0,END)
print(mytext)
if not vals:
self.regcombo.configure(values = (mytext,))
elif mytext not in vals:
with open('regions1.txt','a') as f:
f.write('\n'+ mytext)
self.regcombo.configure(values = vals + (mytext,))
f.close
return 'break'
app = MainWindow(root)
root.mainloop()

My canvas doesnt let me scroll python

I was creating a youtube manager for my personal use, and it was going decently well, until I hit a brick wall with the UI.
Here's the code:
class Application(ttk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.create_widgets()
def create_widgets(self):
nb = ttk.Notebook(self)
page1 = ttk.Frame(nb, width= 300)
#page1 = ttk.Frame(self, width= 300)
nb.add(page1, text='One')
#page1.grid()
nb.grid()
frames = {}
labels = {}
lk_btns = {}
cmt_btns = {}
mk_wch_btns = {}
dwld_btns = {}
i = 0
scrollbar = ttk.Scrollbar(page1)
#scrollbar.grid(row = 0,column = 1,sticky = "ns")
scrollbar.pack(side = "right",fill = "y")
listbox = tk.Canvas(page1,yscrollcommand = scrollbar.set,)
#listbox.grid(row = 0,column = 0,sticky = "nsew")
listbox.pack(side = "left",fill = "both")
for v in test.getLatestVids():
frm = ttk.Frame(listbox)
frm.grid(row=i, column=1,sticky = "E")
lb = ttk.Label(frm, text=convert65536(v["snippet"]["title"]))
lb.grid(row = 0,rowspan = 4,column=0,sticky = "E")
labels[i] = lb
download = ttk.Button(frm, text="Download")
download.grid(row = 1,column=1,sticky = "W")
dwld_btns[i] = download
mwatched = ttk.Button(frm, text="Mark Watched")
mwatched.grid(row = 2,column=1,sticky = "W")
mk_wch_btns[i] = mwatched
like = ttk.Button(frm, text="Like")
like.grid(row = 1,column=2,sticky = "W")
lk_btns[i] = like
comment = ttk.Button(frm, text="Comment")
comment.grid(row = 2,column=2,sticky = "W")
cmt_btns[i] = comment
frames[i] = frm
i += 1
scrollbar.config(command=listbox.yview)
listbox.configure(scrollregion=(-400, -400, 400, 400))
#self.quit = tk.Button(self, text="QUIT", fg="red",
# command=root.destroy)
root = tk.Tk()
app = Application(master=root)
app.mainloop()
PS1: The "convert65536" method is a method for handling the emojis in youtube titles. I found it here.
PS2: The "test.getLatestVids" method is an external method I made that returns a list of dictionaries.
The problem here is that the code thinks there is a lot of space, and does'nt use the scrollbar instead. I tried to make this work by limiting the size of "page1", but I failed to do so.
Here are some screenshots:
In fullscreen
Seeing the entire list thanks to multiple desktops
Items added to a canvas with pack, place or grid will not scroll. The only things that will scroll on a canvas are windows added with create_window.
The most common solution is to add one frame to the canvas, and then pack, place, or grid widgets inside the frame. See http://stackoverflow.com/a/3092341/7432.
If you're creating a vertical list of text and widgets, an even simpler solution is to use a text widget, which allows you to embed widgets with the window_create method.

Categories