Dynamically add columns in Tkinter treeview - python

The program is supposed to add columns dynamically in order to present more data when corresponding button is pressed. The buttons can be pressed in different order and that effects the appearance of the next column/header. As you can see in the example program, the headers are not updated correctly. Only the last one is shown in the table. If the item (selected row and column) already has data, it should be updated, but currently the data is added only in new column, so one of the questions is how to update an item referred by selected row and header. When the row is deleted, all empty columns should be removed.
I'm trying to update the columns by concatenating tuples, but have no idea how to deal with the headers.
Any suggestions are very appreciated.
from random import randint
from tkinter import *
from tkinter.ttk import Treeview
def get_window():
root = Tk()
root.resizable(width=True, height=True)
root.geometry("823x458")
root.title("PsControl")
return root
def get_top_frame(root):
frame = Frame(root)
frame.name = 'top_frame'
frame.root = root
frame.pack(side=TOP, expand=False, fill=X)
button1 = Button(frame, text="Add Row", command=lambda: tv.insert('', 'end', text="hostname"))
button1.grid(row=0, column=1)
button1 = Button(frame, text="Add Cow 1", command=lambda: tv_insert("H1", randint(1, 100)))
button1.grid(row=0, column=2)
button2 = Button(frame, text="Add Cow 2", command=lambda: tv_insert("H2", randint(1, 100)))
button2.grid(row=0, column=3)
button3 = Button(frame, text="Add Cow 3", command=lambda: tv_insert("H3", randint(1, 100)))
button3.grid(row=0, column=4)
button4 = Button(frame, text="Add Cow 20", command=lambda: tv_insert("H4", randint(1, 100)))
button4.grid(row=0, column=5)
button5 = Button(frame, text="Delete row", command=lambda: tv.delete(tv.selection()))
button5.grid(row=0, column=6)
def get_bottom_frame(root):
global tv
frame = Frame(root, highlightbackground='#3E4149', highlightthickness=1, borderwidth=2)
frame.name = 'bottom_frame'
frame.root = root
h = Scrollbar(root, orient='horizontal')
h.pack(side=BOTTOM, fill=X)
v = Scrollbar(root)
v.pack(side=RIGHT, fill=Y)
frame.pack(side=BOTTOM, expand=True, fill=BOTH)
frame.config(background='#FFFFFF')
tv = Treeview(frame, xscrollcommand=h.set, yscrollcommand=v.set)
tv.column("#0", width=135, minwidth=35, stretch=NO)
tv.heading("#0", text='Host', anchor='w')
tv.pack(expand=True, fill='both')
h.config(command=tv.xview)
v.config(command=tv.yview)
def tv_insert(heading, insert_data):
selection = tv.selection()
columns = tv["columns"]
if columns == '':
tv["columns"] = (heading,)
tv.column(heading, width=135, minwidth=35, stretch=NO)
tv.heading(heading, text=heading, anchor='w')
tv.item(selection, values=insert_data)
else:
new_col = columns + (heading,)
tv["columns"] = new_col
tv.heading(heading, text=heading, anchor='w')
data = tv.item(selection, "values")
if data == '':
tv.item(selection, values=insert_data)
else:
new_data = data + (insert_data,)
tv.item(selection, values=new_data)
def delete_row():
selection = tv.selection()
tv.delete(selection)
root = get_window()
get_top_frame(root)
get_bottom_frame(root)
root.mainloop()
Thanks to #acw1668 answer here is the code that does the job as expected. Any suggestions for improvement are welcome.
def tv_insert(heading, insert_data):
selection = tv.selection()
columns = tv["columns"]
if columns == '': # if no columns, create column, heading and item.
tv["columns"] = (heading,)
tv.column(heading, width=135, minwidth=35, stretch=NO)
tv.heading(heading, text=heading, anchor='w')
tv.item(selection, values=(insert_data,))
else:
headings = [tv.heading(col) for col in columns] # save current headings
if heading not in columns:
new_col = columns + (heading,)
tv["columns"] = new_col
# restore previous headings
for h in headings:
tv.heading(h['text'], text=h['text'], anchor=h['anchor'])
# set new heading
tv.heading(heading, text=heading, anchor='w')
# add data/item with with size of the columns
len_col = len(new_col)
data = ['' for _ in range(len_col)] # Create an empty list
data[len_col - 1] = insert_data # Update the next
tv.item(selection, values=tuple(data))
else:
data = tv.item(selection, "values")
# if heading exist but no item on the the selected row
if data == '':
data = ['' for _ in range(len(headings))]
index = columns.index(heading)
data[index] = insert_data
tv.item(selection, values=tuple(data))
else:
data = list(data)
if len(data) < len(columns):
new_data = ['' for _ in range(len(columns))]
for i, d in enumerate(data):
new_data[i] = d
index = columns.index(heading)
new_data[index] = insert_data
tv.item(selection, values=tuple(new_data))
else:
index = columns.index(heading)
data[index] = insert_data
tv.item(selection, values=tuple(data))

Since you have assigned new columns to tv, the headings information is lost. You should save the current headings information before assigning the new columns and restore them after:
def tv_insert(heading, insert_data):
selection = tv.selection()
columns = tv["columns"]
if columns == '':
tv["columns"] = (heading,)
tv.column(heading, width=135, minwidth=35, stretch=NO)
tv.heading(heading, text=heading, anchor='w')
tv.item(selection, values=insert_data)
else:
headings = [tv.heading(col) for col in columns] # save current headings
new_col = columns + (heading,)
tv["columns"] = new_col
# restore previous headings
for h in headings:
tv.heading(h['text'], text=h['text'], anchor=h['anchor'])
# set new heading
tv.heading(heading, text=heading, anchor='w')
data = tv.item(selection, "values")
if data == '':
tv.item(selection, values=insert_data)
else:
new_data = data + (insert_data,)
tv.item(selection, values=new_data)

Related

Get treeview row and put it into another treeview in another root

I made a checkout function in my library management system. It aims to let the user choose books from the given treeview: I aim to get the row of data that the user clicks on and pressed "add to cart" to another window with a treeview: . Only adding the specific row of books data that they chose from the main page. Here is my treeview code:
reserve_button = Button(text="RESERVE" ,fg=WHITE, bg=NAVY_BLUE, width=20, command=reserve)
reserve_button.grid(column=5,row=5)
checkout_button = Button(text="CHECKOUT", fg=WHITE,bg="#6DA5A9",width=20,command=checkout_page)
checkout_button.grid(column=0,row=5,sticky="w")
search_button = Button(text="SEARCH", fg=WHITE,bg="pink", width=20, command=search)
search_button.grid(column=3,row=3)
add_to_cart = Button(text="ADD TO CART", fg=WHITE,bg="#7E370C", width=20,command=add_to_cart_f)
add_to_cart.grid(column=7, row=5,sticky="e")
tree = ttk.Treeview()
books_data = pandas.read_csv("List of Books - Sheet1 (3).csv")
df_column = books_data.columns.values
print(len(df_column))
print(df_column)
tree["column"] = list(books_data.columns)
tree["show"] = "headings"
vsb = ttk.Scrollbar(orient="vertical", command=tree.yview())
vsb.grid(column=8, row=4, sticky="ns")
tree.configure(yscrollcommand=vsb.set)
for column in tree['column']:
tree.heading(column,text=column)
df_rows = books_data.to_numpy().tolist()
for row in df_rows:
tree.insert("","end",values=row)
tree.grid(column=0,row=4,columnspan=8)
The short answer
I created an example project that would use this:
import tkinter as tk
import tkinter.ttk as ttk
import sys
from numpy import select
import pandas
# https://gist.githubusercontent.com/netj/8836201/raw/6f9306ad21398ea43cba4f7d537619d0e07d5ae3/iris.csv
df = pandas.read_csv('./booklist.csv')
df.columns = df.columns.str.replace('.', '', regex=False)
df.columns = df.columns.str.replace(' ', '_', regex=False)
df.head()
class main_window:
def __init__(self, root):
self.root = root
root.title("Treeview Search Example")
# Create DataFrame for this window
self.build_df = df.copy()
self.checkout_ids = []
# INITIALIZE TREEVIEW + SCROLLVIEW
self.tree = ttk.Treeview(root, columns=list(df.columns.values), show='headings')
self.tree.grid(row=1, column=0, sticky='nsew')
# https://stackoverflow.com/a/41880534/5210078
vsb = ttk.Scrollbar(root, orient="vertical", command=self.tree.yview)
vsb.grid(row=1, column=1, sticky='ns')
self.tree.configure(yscrollcommand=vsb.set)
for column in self.tree['column']:
self.tree.heading(column,text=column)
df_rows = df.to_numpy().tolist()
for row in df_rows:
if row[4] != 0:
self.tree.insert("","end",values=row)
# ADD SEARCH BOXES
search_frame = tk.Frame(root)
search_frame.grid(row=0, column=0, columnspan=2, sticky='nsew')
tk.Label(search_frame, text="TITLE:").grid(row=0, column=0)
tk.Label(search_frame, text="AUTHOR:").grid(row=0, column=2)
tk.Label(search_frame, text="IDENTIFICATION NO:").grid(row=0, column=4)
tk.Label(search_frame, text="SUBJECT CATEGORY:").grid(row=0, column=6)
# Add Search boxes
self.title_ent = tk.Entry(search_frame)
self.title_ent.grid(row=0, column=1)
self.author_ent = tk.Entry(search_frame)
self.author_ent.grid(row=0, column=3)
self.identifaction_ent = tk.Entry(search_frame)
self.identifaction_ent.grid(row=0, column=5)
self.category_ent = tk.Entry(search_frame)
self.category_ent.grid(row=0, column=7)
tk.Button(search_frame, text="Search", command=self.search).grid(row=0, column=10)
tk.Button(search_frame, text="Reseve", command=self.reserve).grid(row=0, column=11)
def search(self):
# https://stackoverflow.com/a/27068344/5210078
self.tree.delete(*self.tree.get_children())
self.build_df = df.copy()
# https://stackoverflow.com/a/56157729/5210078
entries = [
self.title_ent,
self.author_ent,
self.identifaction_ent,
self.category_ent
]
if entries[0].get():
self.build_df = self.build_df[self.build_df.TITLE.str.contains(entries[0].get())]
if entries[1].get():
self.build_df = self.build_df[self.build_df.AUTHOR.str.contains(entries[1].get())]
if entries[2].get():
self.build_df = self.build_df[self.build_df.SUBJECT_CATEGORY.str.contains(entries[2].get())]
if entries[3].get():
self.build_df = self.build_df[self.build_df.PUBLICATION_DATE == (entries[3].get())]
df_rows = self.build_df.to_numpy().tolist()
for row in df_rows:
print(row)
if row[4] != 0:
self.tree.insert("","end",values=row)
def reserve(self):
selected = self.tree.item(self.tree.focus())
if selected['values']:
# get the id
book_id = selected['values'][3]
book_id_val = df.loc[df['IDENTIFICATION_NO'] == book_id, 'BOOK_ITEM'].to_numpy().tolist()[0]
if book_id_val < 1:
return 0
else:
self.checkout_ids.append(book_id)
df.loc[df['IDENTIFICATION_NO'] == book_id, 'BOOK_ITEM'] = book_id_val - 1
self.search()
if __name__ == '__main__':
main = tk.Tk()
main_window(main)
main.mainloop()
sys.exit()
Where booklist.csv looks like the following:
TITLE,AUTHOR,PUBLICATION DATE,IDENTIFICATION NO.,BOOK ITEM,SUBJECT CATEGORY
Book 1,Author 1,1923,001/geo003/1993,4,Awesome
Book 2,Author 2,1924,001/geo003/1994,5,Awesome
Book 3,Author 3,1925,001/geo003/1995,6,Awesome
Book 4,Author 4,1926,001/geo003/1996,7,Awesome
Book 5,Author 5,1927,001/geo003/1997,8,Awesome
Book 6,Author 6,1928,001/geo003/1998,9,Awesome
Book 7,Author 7,1929,001/geo003/1999,10,Awesome
Book 8,Author 8,1930,001/geo003/2000,11,Awesome
Book 9,Author 9,1931,001/geo003/2001,12,Awesome
Book 10,Author 10,1932,001/geo003/2002,13,Awesome
The explanation
df = pandas.read_csv('./booklist.csv')
df.columns = df.columns.str.replace('.', '', regex=False)
df.columns = df.columns.str.replace(' ', '_', regex=False)
Clean-up the csv file, so there are no spaces or . in column names (pandas really doesn't like them and they are not necessary)
class main_window:
def __init__(self, root):
self.root = root
root.title("Treeview Search Example")
# Create DataFrame for this window
self.build_df = df.copy()
self.checkout_ids = []
Initialize a class main_window which contains the code you need. And create two variables build_df and checkout_ids, the checkout_ids will be a list containing your current "basket". The build_df holds a temporary copy of your DataFrame (df), which can be filtered and moved around as you like, without effecting the original DataFrame.
# INITIALIZE TREEVIEW + SCROLLVIEW
self.tree = ttk.Treeview(root, columns=list(df.columns.values), show='headings')
self.tree.grid(row=1, column=0, sticky='nsew')
# https://stackoverflow.com/a/41880534/5210078
vsb = ttk.Scrollbar(root, orient="vertical", command=self.tree.yview)
vsb.grid(row=1, column=1, sticky='ns')
self.tree.configure(yscrollcommand=vsb.set)
for column in self.tree['column']:
self.tree.heading(column,text=column)
df_rows = df.to_numpy().tolist()
for row in df_rows:
if row[4] != 0:
self.tree.insert("","end",values=row)
# ADD SEARCH BOXES
search_frame = tk.Frame(root)
search_frame.grid(row=0, column=0, columnspan=2, sticky='nsew')
tk.Label(search_frame, text="TITLE:").grid(row=0, column=0)
tk.Label(search_frame, text="AUTHOR:").grid(row=0, column=2)
tk.Label(search_frame, text="IDENTIFICATION NO:").grid(row=0, column=4)
tk.Label(search_frame, text="SUBJECT CATEGORY:").grid(row=0, column=6)
# Add Search boxes
self.title_ent = tk.Entry(search_frame)
self.title_ent.grid(row=0, column=1)
self.author_ent = tk.Entry(search_frame)
self.author_ent.grid(row=0, column=3)
self.identifaction_ent = tk.Entry(search_frame)
self.identifaction_ent.grid(row=0, column=5)
self.category_ent = tk.Entry(search_frame)
self.category_ent.grid(row=0, column=7)
tk.Button(search_frame, text="Search", command=self.search).grid(row=0, column=10)
tk.Button(search_frame, text="Reseve", command=self.reserve).grid(row=0, column=11)
Initialize the main UI, this holds things like your entry boxes. Importantly, the if statement: if row[4] != 0: is used because if there are no books available, there is no reason to display them!
def search(self):
# https://stackoverflow.com/a/27068344/5210078
self.tree.delete(*self.tree.get_children())
self.build_df = df.copy()
# https://stackoverflow.com/a/56157729/5210078
entries = [
self.title_ent,
self.author_ent,
self.identifaction_ent,
self.category_ent
]
if entries[0].get():
self.build_df = self.build_df[self.build_df.TITLE.str.contains(entries[0].get())]
if entries[1].get():
self.build_df = self.build_df[self.build_df.AUTHOR.str.contains(entries[1].get())]
if entries[2].get():
self.build_df = self.build_df[self.build_df.SUBJECT_CATEGORY.str.contains(entries[2].get())]
if entries[3].get():
self.build_df = self.build_df[self.build_df.PUBLICATION_DATE == (entries[3].get())]
df_rows = self.build_df.to_numpy().tolist()
for row in df_rows:
print(row)
if row[4] != 0:
self.tree.insert("","end",values=row)
The search system remains the same as the previous answer! Other than the if statement functionality added as seen earlier!
The reserve function
def reserve(self):
selected = self.tree.item(self.tree.focus())
if selected['values']:
# get the id
book_id = selected['values'][3]
book_id_val = df.loc[df['IDENTIFICATION_NO'] == book_id, 'BOOK_ITEM'].to_numpy().tolist()[0]
if book_id_val < 1:
return 0
else:
self.checkout_ids.append(book_id)
df.loc[df['IDENTIFICATION_NO'] == book_id, 'BOOK_ITEM'] = book_id_val - 1
self.search()
selected = self.tree.item(self.tree.focus())
Gets the currently selected item from the self.tree
if selected['values']:
If there is a selected item (stops errors when nothing is selected)
book_id = selected['values'][3]
Takes the book_id (4th column) from the selected item and stores a copy of it.
book_id_val = df.loc[df['IDENTIFICATION_NO'] == book_id, 'BOOK_ITEM'].to_numpy().tolist()[0]
Finds the book in the original DataFrame and get how many books there are of it!
if book_id_val < 1:
return 0
If there are no books available, don't do anything, (this is a fallback error-catcher because it isn't entirely necessary).
self.checkout_ids.append(book_id)
df.loc[df['IDENTIFICATION_NO'] == book_id, 'BOOK_ITEM'] = book_id_val - 1
self.search()
Store a copy of the id into the checkout_ids list
Decrease the number of available books by one
Reload the treeview!

How can I add a search function to a treeview?

I want to display a row that matches the entry from all of my entry boxes into the treeview. How can I get the values of the treeview and check if it matches the entry from one of the boxes and display the whole row. Here is my treeview code
tree = ttk.Treeview()
books_data = pandas.read_csv("List of Books - Sheet1 (3).csv")
df_column = books_data.columns.values
print(len(df_column))
print(df_column)
tree["column"] = list(books_data.columns)
tree["show"] = "headings"
for column in tree['column']:
tree.heading(column,text=column)
df_rows = books_data.to_numpy().tolist()
for row in df_rows:
tree.insert("","end",values=row)
tree.grid(column=0,row=4,columnspan=8)
The short solution
import tkinter as tk
import tkinter.ttk as ttk
import sys
import pandas
# https://gist.githubusercontent.com/netj/8836201/raw/6f9306ad21398ea43cba4f7d537619d0e07d5ae3/iris.csv
df = pandas.read_csv('./iris.csv')
df.head()
class main_window:
def __init__(self, root):
self.root = root
root.title("Treeview Search Example")
# INITIALIZE TREEVIEW + SCROLLVIEW
self.tree = ttk.Treeview(root, columns=list(df.columns.values), show='headings')
self.tree.grid(row=1, column=0, sticky='nsew')
# https://stackoverflow.com/a/41880534/5210078
vsb = ttk.Scrollbar(root, orient="vertical", command=self.tree.yview)
vsb.grid(row=1, column=1, sticky='ns')
self.tree.configure(yscrollcommand=vsb.set)
for column in self.tree['column']:
self.tree.heading(column,text=column)
df_rows = df.to_numpy().tolist()
for row in df_rows:
self.tree.insert("","end",values=row)
# ADD SEARCH BOXES
search_frame = tk.Frame(root)
search_frame.grid(row=0, column=0, columnspan=2, sticky='nsew')
tk.Label(search_frame, text="sepal.length:").grid(row=0, column=0)
tk.Label(search_frame, text="sepal.width:").grid(row=0, column=2)
tk.Label(search_frame, text="petal.length:").grid(row=0, column=4)
tk.Label(search_frame, text="petal.width:").grid(row=0, column=6)
tk.Label(search_frame, text="variety:").grid(row=0, column=8)
# Add Search boxes
self.sepal_length_ent = tk.Entry(search_frame)
self.sepal_length_ent.grid(row=0, column=1)
self.sepal_width_ent = tk.Entry(search_frame)
self.sepal_width_ent.grid(row=0, column=3)
self.petal_length_ent = tk.Entry(search_frame)
self.petal_length_ent.grid(row=0, column=5)
self.petal_width_ent = tk.Entry(search_frame)
self.petal_width_ent.grid(row=0, column=7)
self.variety_ent = tk.Entry(search_frame)
self.variety_ent.grid(row=0, column=9)
tk.Button(search_frame, text="Search", command=self.search).grid(row=0, column=10)
def search(self):
# https://stackoverflow.com/a/27068344/5210078
self.tree.delete(*self.tree.get_children())
build_query = ""
# https://stackoverflow.com/a/56157729/5210078
if self.sepal_length_ent.get():
build_query += f'& {self.sepal_length_ent.get()} == `sepal.length` '
if self.sepal_width_ent.get():
build_query += f'& {self.sepal_width_ent.get()} == `sepal.width` '
if self.petal_length_ent.get():
build_query += f'& {self.petal_length_ent.get()} == `petal.length` '
if self.petal_width_ent.get():
build_query += f'& {self.petal_width_ent.get()} == `petal.width` '
if self.variety_ent.get():
build_query += f'& "{self.variety_ent.get()}" in `variety`'
if build_query:
print(build_query)
queried_df = df.query(build_query[1:])
else:
queried_df = df
df_rows = queried_df.to_numpy().tolist()
for row in df_rows:
self.tree.insert("","end",values=row)
if __name__ == '__main__':
main = tk.Tk()
main_window(main)
main.mainloop()
sys.exit()
The explanation of the solution
import tkinter as tk
import tkinter.ttk as ttk
import sys
import pandas
Obviously importing all the needed programs.
# https://gist.githubusercontent.com/netj/8836201/raw/6f9306ad21398ea43cba4f7d537619d0e07d5ae3/iris.csv
df = pandas.read_csv('./iris.csv')
df.head()
Loading the csv dataset. I used the IRIS dataset because it is easily accessible.
Jump ahead to:
if __name__ == '__main__':
main = tk.Tk()
main_window(main)
main.mainloop()
sys.exit()
Here we load the main_window class into our main tkinter window (makes your code look neater)
The self-explanatory bit (added a ttk treeview with scrollbar):
class main_window:
def __init__(self, root):
self.root = root
root.title("Treeview Search Example")
# INITIALIZE TREEVIEW + SCROLLVIEW
self.tree = ttk.Treeview(root, columns=list(df.columns.values), show='headings')
self.tree.grid(row=1, column=0, sticky='nsew')
# https://stackoverflow.com/a/41880534/5210078
vsb = ttk.Scrollbar(root, orient="vertical", command=self.tree.yview)
vsb.grid(row=1, column=1, sticky='ns')
self.tree.configure(yscrollcommand=vsb.set)
for column in self.tree['column']:
self.tree.heading(column,text=column)
df_rows = df.to_numpy().tolist()
for row in df_rows:
self.tree.insert("","end",values=row)
# ADD SEARCH BOXES
search_frame = tk.Frame(root)
search_frame.grid(row=0, column=0, columnspan=2, sticky='nsew')
tk.Label(search_frame, text="sepal.length:").grid(row=0, column=0)
tk.Label(search_frame, text="sepal.width:").grid(row=0, column=2)
tk.Label(search_frame, text="petal.length:").grid(row=0, column=4)
tk.Label(search_frame, text="petal.width:").grid(row=0, column=6)
tk.Label(search_frame, text="variety:").grid(row=0, column=8)
# Add Search boxes
self.sepal_length_ent = tk.Entry(search_frame)
self.sepal_length_ent.grid(row=0, column=1)
self.sepal_width_ent = tk.Entry(search_frame)
self.sepal_width_ent.grid(row=0, column=3)
self.petal_length_ent = tk.Entry(search_frame)
self.petal_length_ent.grid(row=0, column=5)
self.petal_width_ent = tk.Entry(search_frame)
self.petal_width_ent.grid(row=0, column=7)
self.variety_ent = tk.Entry(search_frame)
self.variety_ent.grid(row=0, column=9)
tk.Button(search_frame, text="Search", command=self.search).grid(row=0, column=10)
def search(self):
# https://stackoverflow.com/a/27068344/5210078
self.tree.delete(*self.tree.get_children())
Delete all the rows in the table
build_query = ""
# https://stackoverflow.com/a/56157729/5210078
if self.sepal_length_ent.get():
build_query += f'& {self.sepal_length_ent.get()} == `sepal.length` '
if self.sepal_width_ent.get():
build_query += f'& {self.sepal_width_ent.get()} == `sepal.width` '
if self.petal_length_ent.get():
build_query += f'& {self.petal_length_ent.get()} == `petal.length` '
if self.petal_width_ent.get():
build_query += f'& {self.petal_width_ent.get()} == `petal.width` '
if self.variety_ent.get():
build_query += f'& "{self.variety_ent.get()}" in `variety`'
Build a search query. Use == for integers/floats and use in when searching inside strings. If you want to read more about queries, read this. If you wanted to do something wacky, like make one of your entries a "lower than" box for integers, you could even substitute in a < or >. It basically makes our life much easier here! Because we can do a query, based off a dynamically changing string!
if build_query:
print(build_query)
queried_df = df.query(build_query[1:])
else:
queried_df = df
If there is no build_query (no inputs in entry boxes), just reload the table with the data from the df. (if build_query: is shorthand for if build_query != '':)
If there is query data, do a query with the query terms.
df_rows = queried_df.to_numpy().tolist()
for row in df_rows:
self.tree.insert("","end",values=row)
Reload the treeview with the data from the "new" queried df!
If you wanted a solution relate to your actual csv and interface, I'd recommend sharing an example of your csv and the gui design.
What the solution became
def search():
# clear the tree of data
tree.delete(*tree.get_children())
entries = [
title_entry,
author_entry,
subject_category_entry,
publication_date_entry
]
build_df = books_data
if entries[0].get():
build_df = build_df[build_df.TITLE.str.contains(entries[0].get())]
if entries[1].get():
build_df = build_df[build_df.AUTHOR.str.contains(entries[1].get())]
if entries[2].get():
build_df = build_df[build_df.SUBJECT_CATEGORY.str.contains(entries[2].get())]
if entries[3].get():
build_df = build_df[build_df.PUBLICATION_DATE == (entries[3].get())]
df_rows = build_df.to_numpy().tolist()
for row in df_rows:
tree.insert("","end",values=row)

How do I save treeview items into user_name(entry.get) and display them in another treeview

I have a function where the user can checkout a book
def checkout(self):
global book_id
global checkout_ids
self.main_page.destroy()
self.checkout_page = Tk()
self.checkout_page.title("Checkout Page")
self.checkout_page.config(padx=20, pady=20, bg="white")
confirm_checkout_button = Button(text="CONFIRM AND CHECKOUT", fg=WHITE, bg=NAVY_BLUE, width=31,
height=2, command=self.thank_you_page)
confirm_checkout_button.grid(column=7, row=0, sticky="e")
self.checkout_tree = ttk.Treeview()
self.build_df = self.books_data.copy()
df_column = self.books_data.columns.values
self.checkout_tree["column"] = list(self.books_data.columns)
self.checkout_tree["show"] = "headings"
vsb = ttk.Scrollbar(orient="vertical", command=self.checkout_tree.yview())
vsb.grid(column=8, row=1, sticky="ns")
self.checkout_tree.configure(yscrollcommand=vsb.set)
for column in self.checkout_tree['column']:
self.checkout_tree.heading(column, text=column)
for x in checkout_ids:
build_df = self.build_df[self.build_df.IDENTIFICATION_NO == (x)]
df_rows = build_df.to_numpy().tolist()
for row in df_rows:
self.checkout_tree.insert("", "end", values=row)
self.checkout_tree.grid(column=0,row=1, columnspan=8)
self.checkout_page.mainloop()
My plan was to save the book_ids to the user_name(entry.get) so the books will only display if it matches the user's entry
def return_book_page(self):
self.borrow_or_return_page.destroy()
return_page = Tk()
return_page.title("Return page")
return_page.config(padx=20,pady=20,bg=WHITE)
list_of_books_to_return = Label(text="List of books to return: ", fg=NAVY_BLUE, bg=WHITE,
font=("Viga", 32, "bold"))
list_of_books_to_return.grid(column=0,row=1, sticky="w")
please_text = Label(text="(Please check your library fines before returning the books)",
fg=NAVY_BLUE ,bg=WHITE, font=("viga", 12,))
please_text.grid(column=0,row=2,sticky="w")
check_fines = Button(text="LIBRARY FINES", fg=WHITE,bg=NAVY_BLUE, width=31,height=2,activebackground=NAVY_BLUE)
check_fines.grid(column=3,row=2, columnspan=2)
return_books = Button(text="RETURN BOOKS", fg=WHITE,bg="#6DA5A9", width=31,height=2, activebackground="#6DA5A9")
return_books.grid(column=6,row=2, columnspan=2)
self.return_book_tree = ttk.Treeview()
self.build_df = self.books_data.copy()
df_column = self.books_data.columns.values
self.return_book_tree["column"] = list(self.books_data.columns)
self.return_book_tree["show"] = "headings"
vsb = ttk.Scrollbar(orient="vertical", command=self.return_book_tree.yview())
vsb.grid(column=8, row=1, sticky="ns")
self.return_book_tree.configure(yscrollcommand=vsb.set)
borrow_dictionary = {
self.name_login.get(): checkout_ids
}
for column in self.return_book_tree['column']:
self.return_book_tree.heading(column, text=column)
for x in borrow_dictionary[self.name_login.get()]:
build_df = self.build_df[self.build_df.IDENTIFICATION_NO == (x)]
df_rows = build_df.to_numpy().tolist()
for row in df_rows:
self.checkout_tree.insert("", "end", values=row)
self.return_book_tree.grid(column=0, row=1, columnspan=8)
return_page.mainloop()
I tried creating a dictionary called borrow_dictionary containing the user_name and the list of ids of the book the user has chosen but I get a key error everytime I save it to a csv or use it in a loop to display the values of the tree
borrow_dictionary = {
self.name_login.get(): checkout_ids
}
for column in self.return_book_tree['column']:
self.return_book_tree.heading(column, text=column)
for x in borrow_dictionary[self.name_login.get()]:
build_df = self.build_df[self.build_df.IDENTIFICATION_NO == (x)]
df_rows = build_df.to_numpy().tolist()
for row in df_rows:
self.checkout_tree.insert("", "end", values=row)
How can I efficiently do this feature?

Python tkinter - problem with scrollbar after adding menu column

I'm working on my application connected with my database.
I had a working wersion of it but it didn't look good so I want to add a left side menu to it.
That's how 'show records' page looked like:
photo 1: https://imgur.com/a/egDQpLG
Code of this part:
def createTable(frame, row):
columns = ('IMIĘ','NAZWISKO','PESEL','EMAIL','NR TELEFONU','NR POLISY','DOSTAWCA','WYGASA\nZA (DNI)','NOTATKA','WYŚLIJ\nSMS','WYŚLIJ\nEMAIL')
column_counter = 0
for column in columns:
column_label = Label(frame, text=column, bg=BG_COLOR, fg=TEXT_COLOR, font="Helvetica 10")
column_label.grid(row=row,column=column_counter,padx=2)
column_counter += 1
def addRecord(frame, row, record):
column = 0
for i in range(13): #12 columns in database
if i in (6,7): #records from data base that I don't use
continue
elif i == 11 and notify==True:
send_sms = Button(frame, text="SMS", bg=BG_COLOR, fg=TEXT_COLOR, font="Helvetica 10", command=partial(sendSMSNotification, [], record[5])) #button that sends sms
send_sms.grid(row=row,column=9,padx=2)
elif i == 12 and notify==True:
send_email = Button(frame, text="EMAIL", bg=BG_COLOR, fg=TEXT_COLOR, font="Helvetica 10", command=partial(sendEmailNotification, [], record[5])) #button that sends EMAIL
send_email.grid(row=row,column=10,padx=2)
else:
result_label = Label(frame, text=record[i], bg=BG_COLOR, fg=TEXT_COLOR, font="Helvetica 10")
result_label.grid(row=row,column=column,padx=2)
column += 1
def showRecords():
destroyWidgets() #clears window
main_frame = Frame(root, bg=BG_COLOR)
main_frame.pack(fill=BOTH, expand=1)
my_canvas = Canvas(main_frame, bg=BG_COLOR)
my_canvas.pack(side=LEFT, fill=BOTH, expand=1)
my_scrollbar = ttk.Scrollbar(main_frame, orient=VERTICAL, command=my_canvas.yview)
my_scrollbar.pack(side=RIGHT, fill=Y)
my_canvas.configure(yscrollcommand=my_scrollbar.set)
my_canvas.bind('<Configure>', lambda e:
my_canvas.configure(scrollregion=my_canvas.bbox("all")))
second_frame = Frame(my_canvas, bg=BG_COLOR)
my_canvas.create_window((0,0), window=second_frame, anchor="nw")
conn = sqlite3.connect(dbname)
c = conn.cursor()
back_button_top = Button(second_frame, text='Powrót', width=135, command=lambda: returnMain())
back_button_top.grid(row=0, column=0, pady=20, padx=20)
result_frame = Frame(second_frame, bg=BG_COLOR)
result_frame.grid(row=1, column=0)
createTable(result_frame, 0) #creates a heading of the table ('IMIĘ, NAZWISKO, PESEL' etc)
c.execute("SELECT * FROM dane")
records = c.fetchall()
row_counter = 2
for record in records:
addRecord(result_frame, row_counter, record) #adds a line of data read from the database, looked like a excel
row_counter+=1
Then I added a simple left sided menu column. So I added these lines to the showrecords function.
showMenu creates a Frame in column 0 and adds some buttons
def showMenu():
menu_frame = Frame(root,bg=BG_COLOR)
menu_frame.grid(row=0,column=0, sticky='ew')
menu_frame.grid_columnconfigure(0, weight=1)
show_records_button = Button(....
def showRecords():
destroyWidgets()
showMenu()
record_frame = Frame(root, bg=BG_COLOR)
record_frame.grid(column=1, row=0,sticky='nsew')
main_frame = Frame(record_frame, bg=BG_COLOR)
main_frame.pack(fill=BOTH, expand=1)
......
photo 2: https://imgur.com/iaRIAd9
And now I'm getting the scrollbar in the center of records column. Do you know how can I move the scroll to the right side of window? I think I have tried to change every possible attribute and nothing helped.
Also the problem of small Frame appears if I add an addition (just like my new version of code but without line with showMenu() Frame without adding a menu. So probably something is wrong with the sizing, but I dont know what.

Get input values from a grid with several Entry widgets on Tkinter

I'm trying to create a sudoku solver. I have my grid with the entries at this point but I don't know how to get the values that the user has put in them.
I have no clue yet as to how I'm going to make the Sudoku solver but I think I'll first have to find a way to store the input in some variable(s) so I can work with them later on.
So my question is, how do I get the values that have been filled into the entries?
This is my code thus far:
from tkinter import *
root = Tk()
root.title('Sudoku Solver')
root.geometry('500x400')
mylabel = Label(root, text='Fill in the numbers and click solve').grid(row=0, column=0, columnspan=10)
# Create the grid
def beg():
global e
cells = {}
for row in range(1, 10):
for column in range(1, 10):
if ((row in (1,2,3,7,8,9) and column in (4,5,6)) or (row in (4,5,6) and column in (1,2,3,7,8,9))):
kleur='black'
else:
kleur='white'
cell = Frame(root, bg='white', highlightbackground=kleur,
highlightcolor=kleur, highlightthickness=2,
width=50, height=50, padx=3, pady=3, background='black')
cell.grid(row=row, column=column)
cells[(row, column)] = cell
e = Entry(cells[row, column], width=4, bg='white', highlightthickness=0, fg='black', relief=SUNKEN)
e.pack()
# Tell the button what to do
def solve():
global e
test = e.get()
print(test)
# Create the buttons and give them a command
clearer = Button(root, text='Clear', command=beg)
solver = Button(root, text='Solve', command=solve)
# Locate the buttons
clearer.grid(row=11, column=3, pady=30)
solver.grid(row=11, column=7, pady=30)
# Run it for the first time
beg()
root.mainloop()
I also tried changing e to e[row, column] but that gave me a syntax error.
Standard rule: if you have many elements then keep them on list or dictionary.
Do the same as with cells
Create dictionary
entries = {}
add to dictionary
entries[(row, column)] = e
and get from dictionary
def solve():
for row in range(1, 10):
for column in range(1, 10):
print(row, column, entries[(row, column)].get() )
# from tkinter import * # PEP8: `import *` is not preferred
import tkinter as tk
# --- functions ---
# Create the grid
def beg():
# remove old widgets before creating new ones
for key, val in cells.items():
print(key, val)
val.destroy()
for row in range(1, 10):
for column in range(1, 10):
if ((row in (1,2,3,7,8,9) and column in (4,5,6)) or (row in (4,5,6) and column in (1,2,3,7,8,9))):
kleur='black'
else:
kleur='white'
cell = tk.Frame(root, bg='white', highlightbackground=kleur,
highlightcolor=kleur, highlightthickness=2,
width=50, height=50, padx=3, pady=3, background='black')
cell.grid(row=row, column=column)
cells[(row, column)] = cell
e = tk.Entry(cell, width=4, bg='white', highlightthickness=0, fg='black', relief='sunken')
e.pack()
entries[(row, column)] = e
# Tell the button what to do
def solve():
for row in range(1, 10):
for column in range(1, 10):
print(row, column, entries[(row, column)].get() )
# --- main ---
entries = {}
cells = {}
root = tk.Tk()
root.title('Sudoku Solver')
root.geometry('500x400')
mylabel = tk.Label(root, text='Fill in the numbers and click solve')
mylabel.grid(row=0, column=0, columnspan=10)
# Create the buttons and give them a command
clearer = tk.Button(root, text='Clear', command=beg)
solver = tk.Button(root, text='Solve', command=solve)
# Locate the buttons
clearer.grid(row=11, column=2, pady=30, columnspan=3) # I added `columnspan=3`
solver.grid(row=11, column=6, pady=30, columnspan=3) # I added `columnspan=3`
# Run it for the first time
beg()
root.mainloop()

Categories

Resources