Duplicating values in text file - python

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()

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()

Linked data from database to a button tkinter

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)

Ttk notebook too many frames causes glitch

I am creating a simple SQLite database viewer in Tkinter/Ttk. It consists of a ttk notebook with a table in each tab. For smaller tables, it loads fine but for tables with 1000+ rows it glitches.
How it should look:How it looks on large tables:(The white part at the top overlays other windows which is a bit weird) Is there a better way to load the tables such that this does not occur?
Code:
import tkinter as tk
import tkinter.filedialog
from tkinter import ttk
import random
class dbTable(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
def populateTable(self, data):
for n1, line in enumerate(data):
if n1 == 0: #Header
bg = "lightgray"
else:
bg = "white"
for n2, item in enumerate(line):
f = tk.Frame(self, highlightthickness = 1, highlightbackground = "black", bg = bg)
f.grid(row = n1, column = n2, sticky = "nesw")
l = tk.Label(f, bg = bg, text = item)
l.pack(padx = 3, pady = 3, fill = "both", expand = True)
if len(data) == 1:
f = tk.Label(self, text = "No data")
f.grid(row = 2, column = 0, columnspan = len(data[0]))
class DatabaseViewer(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("Database viewer")
self.loadData()
self.openViewer()
def loadData(self):
self.tables = ["example"]
self.tableHeaders = {"example":("header1","header2","header3","header4")}
randData = [(n, random.randint(10,20000000), random.randint(10,20000000), random.randint(10,20000000), random.randint(10,20000000)) for n in range(1000)]
self.tableData = {"example":randData}
def openViewer(self):
self.viewerFrm = tk.Frame(self)
self.viewerFrm.pack()
self.viewerNotebook = ttk.Notebook(self.viewerFrm)
self.viewerNotebook.pack()
for table in self.tables:
f = tkinter.Frame(self.viewerNotebook)
self.viewerNotebook.add(f, text = table)
t = dbTable(f)
t.pack(fill = "both", expand = True)
t.populateTable([self.tableHeaders[table]] + self.tableData[table])
app = DatabaseViewer()
app.mainloop()

How to pass the 2D array from one class to another in Tkinter?

I am trying to write my first app using Tkinter. I can't understand at all how it is possible to pass the data on variables in the 2D array (entered by user) from one class to another. Tried to change something, but nothing turned out. I will be very grateful for any help or advice.
from Tkinter import *
date_index = [2017, 2018, 2019, 2020, 2021]
product_name = ['product 1', 'product 2', 'product 3', 'product 4', 'product 5']
class main:
def __init__(self, master):
self.master = master
self.master.title('revenue calc')
Button(self.master, text = 'quantity', command=self.q_button).pack()
Button(self.master, text = 'prices', command=self.p_button).pack()
self.master.mainloop()
def q_button(self):
q_child(self.master)
def p_button(self):
p_child(self.master)
class q_child:
def __init__(self, master):
self.slave = Toplevel(master)
self.slave.title('quantity')
self.corner_frame = Frame(self.slave)
self.corner_frame.grid(row=0, column=0)
self.left_frame = Frame(self.slave)
self.left_frame.grid(row=1, column=0)
self.head_frame = Frame(self.slave)
self.head_frame.grid(row=0, column=1)
self.main_frame = Frame(self.slave)
self.main_frame.grid(row=1, column=1)
self.button_frame = Frame(self.slave)
self.button_frame.grid(row=2, column=1)
for i in range(len(product_name)):
self.testlabel = Label(self.left_frame, text = product_name[i])
self.testlabel.grid(row=i, column=0)
for j in range(len(date_index)):
self.testlabel1 = Label(self.head_frame, width = 5, text = date_index[j])
self.testlabel1.grid(row=0, column=j)
self.q0 = []
for j in range(len(date_index)):
self.q0.append([])
for i in range(len(product_name)):
self.q0[j].append(Entry(self.slave, width = 5, text=""))
self.q0[j][i].grid(row=j, column=i, in_ = self.main_frame)
self.save_q_button = Button(self.button_frame, text = 'save', command = self.save_q_data)
self.save_q_button.pack()
def save_q_data(self):
self.q = []
for j in range(len(date_index)):
self.q.append([])
for i in range(len(product_name)):
self.q[j].append(float(self.q0[j][i].get()))
class p_child:
def __init__(self, master):
self.slave = Toplevel(master)
self.slave.title('prices')
self.corner_frame = Frame(self.slave)
self.corner_frame.grid(row=0, column=0)
self.left_frame = Frame(self.slave)
self.left_frame.grid(row=1, column=0)
self.head_frame = Frame(self.slave)
self.head_frame.grid(row=0, column=1)
self.main_frame = Frame(self.slave)
self.main_frame.grid(row=1, column=1)
self.button_frame = Frame(self.slave)
self.button_frame.grid(row=2, column=1)
for i in range(len(product_name)):
self.testlabel = Label(self.left_frame, text = product_name[i])
self.testlabel.grid(row=i, column=0)
for j in range(len(date_index)):
self.testlabel1 = Label(self.head_frame, width = 5, text = date_index[j])
self.testlabel1.grid(row=0, column=j)
self.p0 = []
for j in range(len(date_index)):
self.p0.append([])
for i in range(len(product_name)):
self.p0[j].append(Entry(self.slave, width = 5, text=""))
self.p0[j][i].grid(row=j, column=i, in_ = self.main_frame)
self.save_p_button = Button(self.button_frame, text = 'save', command = self.save_p_data)
self.save_p_button.pack()
def save_p_data(self):
self.rev = []
self.revall = []
self.p = []
for j in range(len(date_index)):
self.rev.append([])
self.p.append([])
self.s = 0
for i in range(len(product_name)):
self.p[j].append(float(self.p0[j][i].get()))
self.rev[j].append(self.p[j][i]*q[j][i]) # NameError: global name 'q' is not defined
self.s += self.rev[j][i]
self.revall.append(self.s)
root = Tk()
main(root)
See below a simplified version of your code which shows how to pass data (in this case the text of a single Entry box) from your TopLevel() window back to your main window.
Basically, in your q_child class, you store the data you want to return in an attribute called, for example, self.data, so that when you return to the main class, you can access it by calling q.data.
You can even store this data in the main window's master attribute under a name like q_data, so that it can be accessed in the p_child class, through master.q_data
import Tkinter as tk
class main:
def __init__(self, master):
self.master = master
self.master.q_data = "No data entered"
tk.Button(self.master, text='quantity', command=self.q_button).pack()
tk.Button(self.master, text='prices', command=self.p_button).pack()
self.master.mainloop()
def q_button(self):
# Create a TopLevel window to get user input
q = q_child(self.master)
# Wait for the user to close the TopLevel window
self.master.wait_window(q.slave)
# Store the data input by the user in the main window's "master" attribute
self.master.q_data = q.data
def p_button(self):
# Create a TopLevel window to use the user input data
p = p_child(self.master)
# Wait for the user to close the TopLevel window
self.master.wait_window(p.slave)
class q_child:
def __init__(self, master):
# Create a TopLevel window, and grab focus
self.slave = tk.Toplevel(master)
self.slave.grab_set()
# Add an Entry box and a button
self.q_entry = tk.Entry(self.slave, text="")
self.q_entry.pack()
tk.Button(self.slave, text='save', command=self.save_q_data).pack()
# Initialize the data to be returned
self.data = "No data entered"
def save_q_data(self):
# Update the data to be returned with the Entry box content
self.data = self.q_entry.get()
# Close the TopLevel window
self.slave.destroy()
class p_child:
def __init__(self, master):
# Create a TopLevel window, and grab focus
self.slave = tk.Toplevel(master)
self.slave.grab_set()
# Retrieve the user-input data from the "master"
q_data = master.q_data
# Show the data on a label
tk.Label(self.slave, text=q_data).pack()
# Add a button to go back
tk.Button(self.slave, text='back', command=self.slave.destroy).pack()
root = tk.Tk()
main(root)
Important: self.master.wait_window(q.slave) ensures that the main class waits for the TopLevel window to be closed before continuing to run.

Create n number of windows in tkinter

I am writing a programm in which I am trying to open a n number of windows. My code is:
from tkinter import *
from tkinter import ttk
class Main_window(ttk.Frame):
"""A program"""
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
"""Creates all the objects in the window"""
self.min_lbl = ttk.Label(self, text = "1").grid(row = 0, column = 0,
sticky = W)
self.max_lbl = ttk.Label(self, text = "100").grid(row = 0, column = 2,
sticky = W)
spinval = IntVar()
self.scale = ttk.Scale(self, orient = HORIZONTAL,
length = 200,
from_ = 1, to = 100,
variable = spinval,
command=self.accept_whole_number_only)
self.scale.grid(row = 0,column = 1,sticky = W)
self.spinbox = Spinbox(self, from_ = 1, to = 100,
textvariable = spinval,
command = self.update,
width = 10)
self.spinbox.grid(row = 0,column =3,sticky = W)
self.go_bttn = ttk.Button(self, text = "Go",
command = self.create_windows
).grid(row = 1, column = 1, sticky = W)
def accept_whole_number_only(self, e=None):
"""Makes the numbers from the scale whole"""
value = self.scale.get()
if int(value) != value:
self.scale.set(round(value))
def update(self):
"""Updates the scale and spinbox"""
self.scale.set(self.spinbox.get())
def create_windows(self):
"""This function will create all the new windows"""
value = self.scale.get()
window_num = value
negative_window_num = 1
while window_num != 0:
root = Tk()
root.title("This is Window "+str(window_num)[:-2]+" of "+str(value)[:-2])
root.geometry("350x200")
app = Window_creator(root)
root.mainloop()
window_num -= 1
class Window_creator(ttk.Frame):
"""makes child windows"""
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
"""creates all the widgets in the window"""
def main():
"""Loops the window"""
root = Tk()
root.title("Programm")
root.geometry("350x200")
app = Main_window(root)
root.mainloop()
main()
What I want this code to do is I want to be able to set the spinbox or scale to number n and then when I click the Button i want n numbers of child windows to appear. I tried this with a while loop but it doesn't quite work like I want it to by creating a new window just after the I closed the prevoius window. You also have to close the main window first for it to work (I am going to make the button close the window automatically later). Any Ideas on how I could make this work?
Call child = Toplevel(), instead of root = Tk().
Also, you can not call mainloop more than once (since there should be only one event loop).
from tkinter import *
from tkinter import ttk
class Main_window(ttk.Frame):
"""A program"""
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
"""Creates all the objects in the window"""
self.min_lbl = ttk.Label(self, text = "1").grid(row = 0, column = 0,
sticky = W)
self.max_lbl = ttk.Label(self, text = "100").grid(row = 0, column = 2,
sticky = W)
spinval = IntVar()
self.scale = ttk.Scale(self, orient = HORIZONTAL,
length = 200,
from_ = 1, to = 100,
variable = spinval,
command=self.accept_whole_number_only)
self.scale.grid(row = 0,column = 1,sticky = W)
self.spinbox = Spinbox(self, from_ = 1, to = 100,
textvariable = spinval,
command = self.update,
width = 10)
self.spinbox.grid(row = 0,column =3,sticky = W)
self.go_bttn = ttk.Button(self, text = "Go",
command = self.create_windows
).grid(row = 1, column = 1, sticky = W)
def accept_whole_number_only(self, e=None):
"""Makes the numbers from the scale whole"""
value = self.scale.get()
if int(value) != value:
self.scale.set(round(value))
def update(self):
"""Updates the scale and spinbox"""
self.scale.set(self.spinbox.get())
def create_windows(self):
"""This function will create all the new windows"""
value = self.scale.get()
window_num = value
negative_window_num = 1
for n in range(int(window_num)):
child = Toplevel()
child.title("This is Window "+str(window_num)[:-2]+" of "+str(value)[:-2])
child.geometry("350x200")
app = Window_creator(child)
class Window_creator(ttk.Frame):
"""makes child windows"""
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
"""creates all the widgets in the window"""
def main():
"""Loops the window"""
root = Tk()
root.title("Programm")
root.geometry("350x200")
app = Main_window(root)
root.mainloop()
main()

Categories