How can I add a search function to a treeview? - python

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)

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!

Dynamically add columns in Tkinter treeview

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)

Store variables entered in tkinter form

I am creating a Tkinter-based form where I want to store each item that the user types in as a separate variable. I understand how to generate the form, but I am lost on how to handle the program after the user presses the Enter button. I really just need everything stored as a string.
from tkinter import *
import pandas as pd
fields = ('Event', 'Event Folder', 'Session', 'Date: (MM/DD/YYYY)', 'StartTime: 24HR(HH:MM)', 'EndTime: 24HR(HH:MM)')
def saveVars(entries):
locals().update(entries)
return
def makeform(root, fields):
entries = {}
for field in fields:
row = Frame(root)
lab = Label(row, width=22, text=field+": ", anchor='w')
ent = Entry(row)
ent.insert(0,"")
row.pack(side = TOP, fill = X, padx = 5 , pady = 5)
lab.pack(side = LEFT)
ent.pack(side = RIGHT, expand = YES, fill = X)
entries[field] = ent
return entries
if __name__ == '__main__':
root = Tk()
ents = makeform(root, fields)
b1 = Button(root, text = 'Enter', command = lambda e = ents: saveVars(e))
b1.pack(side = LEFT, padx = 5, pady = 5)
root.mainloop()
What you need to do is build a function that does something with your entry fields. That said you may want to change up you code a little bit to make this easier. Instead of building your labels and entry fields in a function build them in the global namespace and then store the entry fields in a list.
import tkinter as tk
fields = ('Event', 'Event Folder', 'Session', 'Date: (MM/DD/YYYY)',
'StartTime: 24HR(HH:MM)', 'EndTime: 24HR(HH:MM)')
def do_something_with_entries():
for ndex, entry in enumerate(entry_list):
print(fields[ndex], ': ', entry.get())
if __name__ == '__main__':
root = tk.Tk()
entry_list = []
for field in fields:
row = tk.Frame(root)
lab = tk.Label(row, width=22, text=field + ": ", anchor='w')
ent = tk.Entry(row)
entry_list.append(ent)
row.pack(side='top', fill='x', padx=5, pady=5)
lab.pack(side='left')
ent.pack(side='right', expand='yes', fill='x')
tk.Button(root, text='Enter', command=do_something_with_entries).pack(side='left', padx=5, pady=5)
root.mainloop()
Results:
Here is an example using pandas:
import tkinter as tk
import pandas as pd
fields = ['Event', 'Event Folder', 'Session', 'Date: (MM/DD/YYYY)', 'StartTime: 24HR(HH:MM)', 'EndTime: 24HR(HH:MM)']
df = pd.DataFrame(columns=fields)
def do_something_with_entries():
global df
stored_values = []
for ndex, entry in enumerate(entry_list):
stored_values.append(entry.get())
series = pd.Series(stored_values, index=fields)
df = df.append(series, ignore_index=True)
if __name__ == '__main__':
root = tk.Tk()
entry_list = []
for field in fields:
row = tk.Frame(root)
lab = tk.Label(row, width=22, text=field + ": ", anchor='w')
ent = tk.Entry(row)
entry_list.append(ent)
row.pack(side='top', fill='x', padx=5, pady=5)
lab.pack(side='left')
ent.pack(side='right', expand='yes', fill='x')
tk.Button(root, text='Enter', command=do_something_with_entries).pack(side='left', padx=5, pady=5)
root.mainloop()
You are storing the questions as keys and Entry widgets as values of the entries dict.
Firstly, put the entries dict out of the makeform function (otherwise, only makeform function would be able to use it)
Secondly, create the answer dict. We are going to store the answers here.
Thirdly, create a fetch function which is to be called when the user clicks Enter button. It will go through the entries dict and set the values of the answers dict to the entered answers (using Entry.get(...) method)
Now you can process the form answers.
Here is an example:
from tkinter import *
import pandas as pd
fields = ('Event', 'Event Folder', 'Session', 'Date: (MM/DD/YYYY)',
'StartTime: 24HR(HH:MM)', 'EndTime: 24HR(HH:MM)')
entries = {}
answers = {}
def makeform(root, fields):
for field in fields:
row = Frame(root)
lab = Label(row, width=22, text=field + ": ", anchor='w')
ent = Entry(row)
row.pack(side=TOP, fill=X, padx=5, pady=5)
lab.pack(side=LEFT)
ent.pack(side=RIGHT, expand=YES, fill=X)
entries[field] = ent
return entries
def fetch():
for question in entries:
answers[question] = entries[question].get()
print(answers) # do something with the results now
if __name__ == '__main__':
root = Tk()
ents = makeform(root, fields)
root.bind('<Return>', fetch)
b1 = Button(root, text='Enter', command=fetch)
b1.pack(side=LEFT, padx=5, pady=5)
root.mainloop()
PS: You don't need to use a lambda-function as the button command. Simply use the fetch function as a command.
PS 1: And why do you call ent.insert(0, "")? It has no effect.

Select listbox items with button (tkinter)

when i click on each of the items in listbox, a text is printed which comes from db in the Textbox widget of my app. i want to be able to do exactly that also with my button. I mean, when user searches the word in entrybox and the list lowers down to 1 item, i want to be able to select that item with 3 ways (clicking on it in the list, clicking on my button, pressing the Return key on my keyboard). now clicking works. how should i config the button event in enter_meaning function?
import sqlite3 as sqlite
import tkinter as tk
from tkinter import ttk
# GUI Widgets
class EsperantoDict:
def __init__(self, master):
self.search_var = tk.StringVar()
self.search_var.trace("w", lambda name, index, mode: self.update_list())
self.frame_header = ttk.Frame(master, relief=tk.FLAT)
self.frame_header.config(style="TFrame")
self.frame_header.pack(side=tk.TOP, padx=5, pady=5)
ttk.Label(self.frame_header, image=self.small_logo).grid(row=0, column=0, stick="ne", padx=5, pady=5, rowspan=2)
ttk.Label(self.frame_header, text='EsperantoDict', font=('Arial', 18, 'bold')).grid(row=0, column=1)
self.frame_content = ttk.Frame(master)
self.frame_content.config(style="TFrame")
self.frame_content.pack()
self.entry_search = ttk.Entry(self.frame_content, textvariable=self.search_var, width=30)
self.entry_search.bind('<FocusIn>', self.entry_delete)
self.entry_search.bind('<FocusOut>', self.entry_insert)
self.entry_search.grid(row=0, column=0, padx=5)
self.entry_search.focus()
self.entry_search.bind("<KeyRelease>", self.edit_input)
self.button_search = ttk.Button(self.frame_content, text=u"Serĉu", command=self.enter_meaning)
self.photo_search = tk.PhotoImage(file=r'C:\EsperantoDict\search.png')
self.small_photo_search = self.photo_search.subsample(3, 3)
self.button_search.config(image=self.small_photo_search, compound=tk.LEFT, style="TButton")
self.button_search.grid(row=0, column=2, columnspan=1, sticky='nw', padx=5)
self.button_search.bind('<Return>')
self.listbox = tk.Listbox(self.frame_content, height=30, width=30)
self.listbox.grid(row=1, column=0, padx=5)
self.scrollbar = ttk.Scrollbar(self.frame_content, orient=tk.VERTICAL, command=self.listbox.yview)
self.scrollbar.grid(row=1, column=1, sticky='nsw')
self.listbox.config(yscrollcommand=self.scrollbar.set)
self.listbox.bind('<<ListboxSelect>>', self.enter_meaning)
self.textbox = tk.Text(self.frame_content, relief=tk.GROOVE, width=60, height=30, borderwidth=2)
self.textbox.config(wrap='word')
self.textbox.grid(row=1, column=2, sticky='w', padx=5)
# SQLite
self.db = sqlite.connect(r'C:\EsperantoDict\test.db')
self.cur = self.db.cursor()
self.cur.execute("SELECT Esperanto FROM Words ORDER BY Esperanto")
for row in self.cur:
self.listbox.insert(tk.END, row)
self.update_list()
def update_list(self):
self.listbox.delete(0, tk.END)
search_term = self.search_var.get().lower()
if search_term == 'type to search':
search_term = ''
self.cur.execute("SELECT Esperanto FROM Words WHERE LOWER(Esperanto) LIKE ? ORDER BY Esperanto",
('%' + search_term + '%',))
for row in self.cur:
item = row[0]
self.listbox.insert(tk.END, item)
for row in range(0, self.listbox.size(), 2):
self.listbox.itemconfigure(row, background="#f0f0ff")
def edit_input(self, tag):
word_to_esp = {'gx': 'ĝ', 'cx': 'ĉ', 'hx': 'ĥ', 'jx': 'ĵ', 'ux': 'ŭ', 'sx': 'ŝ'}
user_input = self.entry_search.get()
user_input = user_input.lower()
for i in word_to_esp:
if user_input.__contains__(i):
a = user_input.replace(i, word_to_esp[i])
return self.search_var.set(a)
def enter_meaning(self, tag=None):
index = self.listbox.curselection()
esperanto = self.listbox.get(index)
results = self.cur.execute("SELECT English FROM Words WHERE Esperanto = ?", (esperanto,))
for row in results:
self.textbox.delete(1.0, tk.END)
self.textbox.insert(tk.END, row[0])
def entry_delete(self, tag):
if self.entry_search.get():
self.entry_search.delete(0, tk.END)
self.textbox.delete(1.0, tk.END)
return None
def entry_insert(self, tag):
if self.entry_search.get() == '':
self.entry_search.insert(0, "Type to Search")
return None
def main():
root = tk.Tk()
EsperantoDict(root)
root.mainloop()
if __name__ == '__main__': main()
# db tbl name: Words
# db first field name: Esperanto
# db second field name: English

How to fetch one row and display it on textbox and increment it automatically

i wrote this python 3 program, i want to for each row in field Esperanto, get it's corresponding row in the other field in database named English. for example: when i click on row 1 in listbox, i want the text of the row 1 in English field of my db to be displayed on the textbox. now it fetches all the rows and it's a disaster, how can i do that? thanks.
#! /usr/bin/env python3
#GeologyDict by Ali M
import sqlite3 as sqlite
import tkinter as tk
from tkinter import Text
from tkinter import Entry
from tkinter import Scrollbar
from tkinter import ttk
#GUI Widgets
class EsperantoDict:
def __init__(self, master):
master.title("EsperantoDict")
master.resizable(False, False)
master.configure(background='#EAFFCD')
self.style = ttk.Style()
self.style.configure("TFrame", background='#EAFFCD')
self.style.configure("TButton", background='#EAFFCD')
self.style.configure("TLabel", background='#EAFFCD')
self.frame_header = ttk.Frame(master, relief=tk.FLAT)
self.frame_header.pack(side=tk.TOP, padx=5, pady=5)
self.logo = tk.PhotoImage(file=r'C:\EsperantoDict\eo.png')
self.small_logo = self.logo.subsample(10, 10)
ttk.Label(self.frame_header, image=self.small_logo).grid(row=0, column=0, stick="ne", padx=5, pady=5, rowspan=2)
ttk.Label(self.frame_header, text='EsperantoDict', font=('Arial', 18, 'bold')).grid(row=0, column=1)
self.frame_content = ttk.Frame(master)
self.frame_content.pack()
self.entry_search = ttk.Entry(self.frame_content)
self.entry_search.grid(row=0, column=0)
self.entry_search.insert(tk.END, "Type to Search")
self.entry_search.bind('<Button-1>', self.entry_delete)
self.button_search = ttk.Button(self.frame_content, text="Search")
self.aks = tk.PhotoImage(file=r'C:\EsperantoDict\search.png')
self.small_aks = self.aks.subsample(3, 3)
self.button_search.config(image=self.small_aks, compound=tk.LEFT)
self.button_search.grid(row=0, column=1, columnspan=2)
self.listbox = tk.Listbox(self.frame_content, height=28)
self.listbox.grid(row=1, column=0)
self.scrollbar = ttk.Scrollbar(self.frame_content, orient=tk.VERTICAL, command=self.listbox.yview)
self.scrollbar.grid(row=1, column=1, sticky='ns')
self.listbox.config(yscrollcommand=self.scrollbar.set)
self.listbox.bind('<<ListboxSelect>>', self.enter_meaning)
self.textbox = tk.Text(self.frame_content, width=60, height=27)
self.textbox.grid(row=1, column=2)
# SQLite
self.db = sqlite.connect(r'C:\EsperantoDict\test.db')
self.cur = self.db.cursor()
self.cur.execute('SELECT Esperanto FROM Words')
for row in self.cur:
self.listbox.insert(tk.END, row)
# SQLite
def enter_meaning(self, tag):
if self.listbox.curselection():
results = self.cur.execute("SELECT English FROM Words")
for row in results:
self.textbox.insert(tk.END, row)
def entry_delete(self, tag):
self.entry_search.delete(0, tk.END)
return None
def main():
root = tk.Tk()
esperantodict = EsperantoDict(root)
root.mainloop()
if __name__ == '__main__': main()
#db table name: Words
##db first field name: Esperanto
##db second field name: English

Categories

Resources