Sorting entries in a ttk treeview automaticly after inserting - python-3.x

Is there a way to achieve a similar result like in this post python ttk treeview sort numbers but without pressing on the heading? Best way would be right after an item is inserted.

If you are going to sort your contents every time you insert a new item, then a more efficient approach would be to insert the item in the right place rather than sorting the whole data for each insertion.
Here's one way to achieve this using the standard bisect module, whose bisect(a, x) function gives you the index at which x should be inserted in a to maintain order (a is assumed to be sorted). In my example below:
my whole interface is stored in some class GUI;
my treeview is called self.tree and features only one column;
my insert_item method inserts a new line below a certain category in the tree (pointed to by location), and items below each category must be sorted separately in my application, which is why I only retrieve that category's children .
from bisect import bisect
# ... class GUI contains the treeview self.tree ...
def insert_item(self, location, item_name, item_id):
"""Inserts a new item below the provided location in the treeview,
maintaining lexicographic order wrt names."""
contents = [
self.tree.item(child)["text"]
for child in self.tree.get_children(location)
]
self.tree.insert(
location, bisect(contents, item_name), item_id, text=item_name
)

To sort the Treeview after each new item insertion, just add an explicit call to the sorting function at the correct position in your code.
Example code:
import tkinter as tk
from tkinter import ttk
counter = 0
numbers = ['1', '10', '11', '2', '3', '4', '24', '12', '5']
def sort_treeview():
content = [(tv.set(child, column), child)
for child in tv.get_children('')]
try:
content.sort(key=lambda t: int(t[0]))
except:
content.sort()
for index, (val, child) in enumerate(content):
tv.move(child, '', index)
def add_item():
global counter
if counter < 8:
tv.insert('', 'end', values=numbers[counter])
counter += 1
# Sort the treeview after the new item was inserted
# -------------------------------------------------
sort_treeview()
root = tk.Tk()
column = 'number'
tv = ttk.Treeview(root, columns=column, show='headings')
tv.pack()
button = tk.Button(root, text='Add entry', command=add_item)
button.pack()
root.mainloop()

Related

How to get all values from loop in python

I created multiple checkbuttons using a for loop from a list of names. Since there are more than 500 names, I wanted to use the for loop instead of typing them one by one. I need to find out which names are selected from these checkbuttons. But no matter what I did, I couldn't get the values of the checkbuttons one by one. In some of my attempts, I got a single numeric value, but I couldn't get a value for each checkbutton. I don't know where I am doing wrong. Can I get each value from inside this loop? Or do I have to write them all one by one?
## a list as an example (There are more than 500 people on the original list.)
name_list = ['John Smith', 'Granny Smith', 'Michael Smith', 'Big Smith', 'Hello Smith']
for record in name_list:
nameVar = IntVar()
cb_name = Checkbutton(root, text=record, variable=nameVar, bg="white", anchor="w")
cb_name.pack(fill="both")
You can achieve this by creating a dictionary that contains all record names and their corresponding state (0 for unselected and 1 for selected):
from tkinter import *
root = Tk()
name_list = ['John Smith', 'Granny Smith', 'Michael Smith', 'Big Smith', 'Hello Smith']
check_dict = {} # This dictionary will contain all names and their state (0 or 1) as IntVar
def getSelected():
# Check the state of all check_dict elements and return the selected ones as a list
selected_names = []
for record in check_dict:
if check_dict[record].get():
selected_names.append(record)
return selected_names
# Create the checkbuttons and complet the check_dict
for record in name_list:
nameVar = IntVar()
cb_name = Checkbutton(root, text=record, variable=nameVar, bg="white", anchor="w")
cb_name.pack(fill="both")
check_dict[record] = nameVar
# A button to print the selected names
Button(root, text="Show", command=lambda: print(getSelected())).pack()
root.mainloop()
In my code exemple you can call the getSelected() function to get a list of the selected record names.

How can the cursur location be set to a specific cell in tkinter grid?

I have a program for editing csv files, it works nicely other than that whenever you open a file, the cursor goes to the upper left hand corner (i.e. [0][0]). I want it to be in the bottom left hand corner (I have my reasons). I've looked around a bit, but I couldn't find anything to set the cursor position.
Here is a screenshot of the associated window with some random data:
Thanks in advance!
Edit:
Note: The original code isn't mine, I'm just modifying it, so I don't know what everything does.
Assuming you have a two-dimensional list of entries, it's just a matter of setting the focus to the first widget in the last row with focus_set:
last_entry = self.entries[-1][0]
last_entry.focus_set()
If you don't have a two-dimensional list of entries, then use whatever data structure you have to get the first entry on the last row, and then call focus_set() on it.
Here's a working example:
import tkinter as tk
import csv
from io import StringIO
class CsvViewer(tk.Frame):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
def load(self, csv_reader):
self.entries = []
for row_num, row in enumerate(csv_reader):
entry_row = []
self.entries.append(entry_row)
for column_num, column in enumerate(row):
entry = tk.Entry(self, width=20)
entry.insert("end", column)
entry_row.append(entry)
entry.grid(row=row_num, column=column_num, sticky="nsew")
# set focus to the last widget
last_entry = self.entries[-1][0]
last_entry.focus_set()
root = tk.Tk()
viewer = CsvViewer(root)
viewer.pack(fill="both", expand=True)
data = """
First, Last, user
Bob, Johnson, bjohn
Mark, Phillips, mphil
""".strip()
f = StringIO(data)
reader = csv.reader(f, delimiter=",")
viewer.load(reader)
root.mainloop()

list.get() tkinter entry returns 'list' has no attribute 'get'

I've been playing around with tkinter and loving what I can do. I'm a total noob, however, and wanting to make my own matrix multiplier I've run into numerous problems. My goal is to create two grids of tk entries and be able to call them back later with .get, when I call txt_ent_a.get() I get an error: list has no attribute 'get'.
I found these other questions that have helped me get this far:
List of lists changes reflected across sublists unexpectedly
How can I create a list of entries in tkinter
tkinter: Button instance has no attribute 'get'
And this one that is basically the same as my question, though I didn't really understand how to take the conceptual answer and convert it into code:
Saving variables in 'n' Entry widgets Tkinter interface
I know what I have created as "entry_list" is just an empty list of lists. My real question is: how do I loop through making entry widgets and key or objects (I'm shaky on the lingo) that I can loop through using .get() on later, which I can then make into a list of lists (a matrix) that I can use in a function?
Because I'm so new to Python, I'm not sure if I just need to add a StringVar somehow, or .pack something somehow... I believe .pack is a tkinter thing. I'll try and keep my code cleanish, thank you for any help.
from tkinter import *
import tkinter as tk
window = Tk()
lbl1 = Label(window, text="# of rows: 3").grid(column=0, row=0)
lbl2 = Label(window, text="# of cols: 2").grid(column=0, row=1)
window.geometry('250x250')
def clickedc(): #create matrix click event
row = (3) #In final version these will be the numbers I loop over when
col = (2) #making the entries in question.
lblA = Label(window, text="Input matrix:").grid(column=(int(col/2)), row=3)
entry_list = [] #This is what I want to be a list I can .get() from later
r = 1
for r in range(int(row)):
entry_list.append([])
c = 1
for c in range(int(col)):
entry_list[-1].append(tk.Entry(window, width=10).grid(column=c, row=4 + r))
def clickeds(): #Solving click event
r = 1
matrix_A = [] #The matrix I want to put the values into, for reference
for r in range(int(row)):
c = 1
for c in range(int(col)):
matrix_A[r - 1:c - 1] = entry_list.get("1.0",'end-1c') #Causes Error
#Here I'll: output a new matrix as a grid of labels.ex: #1 1
btn_s = Button(window, text="Solve", command=clickeds) #1 0
btn_s.grid(column=int(col) + 1, row=3) #0 1
btn_c = Button(window, text="Create", command=clickedc)
btn_c.grid(column=3, row=1)
window.mainloop()
I hope this code is clear enough. Any advice is welcome, I assume I code rather poorly.

How to store in variables current row selection within tree widget to query a database using python

I wonder if it is possible to store in variables the contents from a tree widget row (when it is selected with the mouse) see picture. Basically I want to sync my tree with a database, every time when I insert or delete an element in my tree, my database needs to auto update.
With the insert part it is not a problem , because I have entry widgets, but I don't know how to manage the delete part. Therefore, I wonder if it is possible to do this with some cursor selection function.
I have been trying for a very long time to find a solution for this, I would really appreciate if someone can help me with some hints
Code:
import tkinter
from tkinter import ttk
class cards(tkinter.Frame):
def __init__(self, parent):
tkinter.Frame.__init__(self, parent)
self.parent=parent
self.parent.geometry("800x500")
self.initialize_user_interface()
def initialize_user_interface(self):
self.parent.title("cards")
self.parent.grid_rowconfigure(0,weight=1)
self.parent.grid_columnconfigure(0,weight=1)
self.parent.config(background="lavender")
self.Card_label = tkinter.Label(self.parent, text = "Card type:")
self.Card_entry = tkinter.Entry(self.parent)
self.Card_label.place(x=5,y=5)
self.Card_entry.place(x=70,y=5)
self.SN_label = tkinter.Label(self.parent, text = "SN:")
self.SN_entry = tkinter.Entry(self.parent)
self.SN_label.place(x=5,y=40)
self.SN_entry.place(x=70,y=40)
self.submit_button = tkinter.Button(self.parent, text = "Insert", command = self.insert_data)
self.submit_button.place(x=210,y=15)
self.exit_button = tkinter.Button(self.parent, text = "Exit", command = self.exit)
self.exit_button.place(x=270,y=15)
self.tree = ttk.Treeview( self.parent, columns=('Card Type', 'SN'))
self.tree.heading('#0', text='Nr.')
self.tree.heading('#1', text='Card Type')
self.tree.heading('#2', text='SN')
self.tree.column('#1', stretch=tkinter.YES)
self.tree.column('#2', stretch=tkinter.YES)
self.tree.column('#0', stretch=tkinter.YES)
self.tree.place(x=0,y=100)
self.treeview = self.tree
self.i = 1
def exit(self):
self.master.destroy()
def insert_data(self):
self.treeview.insert('', 'end', text=str(self.i), values=(self.Card_entry.get(), self.SN_entry.get()))
self.i = self.i + 1
def main():
root=tkinter.Tk()
d=cards(root)
root.mainloop()
if __name__=="__main__":
main()
You can use
for item in self.tree.selection():
print(self.tree.item(item, "text"))
print(self.tree.item(item, "values"))
#print(self.tree.item(item))
to see data from all selected rows - you can select more than one row.
You can use it in function assigned to button
or you can use bind() to assign function to mouse click on row.

How to add columns to a tkinter.Listbox?

I am trying to add these panda columns to a Listbox, so they read like this:
New Zealand NZD
United States USD
ETC.
I am using pandas to get the data from a .csv, but when I try and use a for loop to add the items to the list box using insert I get the error
NameError: name 'END' is not defined or
NameError: name 'end' is not defined
Using this code:
def printCSV():
csv_file = ('testCUR.csv')
df = pd.read_csv(csv_file)
print (df[['COUNTRY','CODE']])
your_list = (df[['COUNTRY','CODE']])
for item in your_list:
listbox.insert(end, item)
You could turn the csv file into a dictionary, use the combined country and currency codes as the keys and just the codes as the values, and finally insert the keys into the Listbox. To get the code of the current selection, you can do this: currencies[listbox.selection_get()].
listbox.selection_get() returns the key which you then use to get the currency code in the currencies dict.
import csv
import tkinter as tk
root = tk.Tk()
currencies = {}
with open('testCUR.csv') as f:
next(f, None) # Skip the header.
reader = csv.reader(f, delimiter=',')
for country, code in reader:
currencies[f'{country} {code}'] = code
listbox = tk.Listbox(root)
for key in currencies:
listbox.insert('end', key)
listbox.grid(row=0, column=0)
listbox.bind('<Key-Return>', lambda event: print(currencies[listbox.selection_get()]))
tk.mainloop()

Resources