tkinter Treeview: get selected item values - python

I'm just starting with a small tkinter tree program in python 3.4.
I'm stuck with returning the first value of the row selected.
I have multiple rows with 4 columns and I am calling a function on left-click on a item:
tree.bind('<Button-1>', selectItem)
The function:
def selectItem(a):
curItem = tree.focus()
print(curItem, a)
This gives me something like this:
I003 <tkinter.Event object at 0x0179D130>
It looks like the selected item gets identified correctly.
All I need now is how to get the first value in the row.
tree-creation:
from tkinter import *
from tkinter import ttk
def selectItem():
pass
root = Tk()
tree = ttk.Treeview(root, columns=("size", "modified"))
tree["columns"] = ("date", "time", "loc")
tree.column("date", width=65)
tree.column("time", width=40)
tree.column("loc", width=100)
tree.heading("date", text="Date")
tree.heading("time", text="Time")
tree.heading("loc", text="Loc")
tree.bind('<Button-1>', selectItem)
tree.insert("","end",text = "Name",values = ("Date","Time","Loc"))
tree.grid()
root.mainloop()

To get the selected item and all its attributes and values, you can use the item method:
def selectItem(a):
curItem = tree.focus()
print tree.item(curItem)
This will output a dictionary, from which you can then easily retrieve individual values:
{'text': 'Name', 'image': '', 'values': [u'Date', u'Time', u'Loc'], 'open': 0, 'tags': ''}
Also note that the callback will be executed before the focus in the tree changed, i.e. you will get the item that was selected before you clicked the new item. One way to solve this is to use the event type ButtonRelease instead.
tree.bind('<ButtonRelease-1>', selectItem)

This is a good example of getting information of a row selected in a python tkinter treeview. Allow me to represent a final neat coding discussed here. I use python 3.8
from tkinter import *
from tkinter import ttk
def selectItem(a):
curItem = tree.focus()
print(tree.item(curItem))
root = Tk()
tree = ttk.Treeview(root, columns=("size", "modified"))
tree["columns"] = ("date", "time", "loc")
tree.column("date", width=65)
tree.column("time", width=40)
tree.column("loc", width=100)
tree.heading("date", text="Date")
tree.heading("time", text="Time")
tree.heading("loc", text="Loc")
tree.bind('<ButtonRelease-1>', selectItem)
tree.insert("","end",text = "Name",values = ("Date","Time","Loc"))
tree.grid()
root.mainloop()
Ther result is
{'text': 'Name', 'image': '', 'values': ['Date', 'Time', 'Loc'], 'open': 0, 'tags': ''}
You can copy, paste and try it. It's good.

And if you want to get row as {column name:value pair}:
def selectItem(a):
curRow = tree.set(a)
loc_value = curRow["loc"]
Or you want to get cell value by column name
def selectItem(a):
loc_value = tree.set(a, column="loc")

Related

Python: Save radio Button tkinter values in an Excel file

I'm trying to create a small interface with Tkinter with 4 radio buttons and each time I click on one of the buttons it will print the value of the button, I've managed to do that!
However, I would like the printed value to be saved in an Excel file for example each time I click on one of the buttons in my tkinter window.
Below is the code I used and the result I got:
from tkinter import *
import tkinter as tk
master = Tk()
master.geometry("175x175")
# Tkinter string variable
# able to store any string value
v = IntVar(master, "0") #0 pour qu'aucun des boutons soit sélectioné par défaut
# Dictionary to create multiple buttons
values = {"classe 1" : "1",
"classe 2" : "2",
"classe 3" : "3",
"classe 4" : "4"}
def affiche ():
val = v.get()
print(val)
data = [
['class1', val],
]
labelValue = tk.Label(master,
text="""Choose your class affectation:""",
justify = tk.LEFT,
padx = 20).pack()
for (text, value) in values.items():
Radiobutton(master, text = text, variable = v, command = affiche,
value = value).pack(side =TOP, ipady = 5)
# Infinite loop can be terminated by
# keyboard or mouse interrupt
# or by any predefined function (destroy())
mainloop()
On the right of the capture below are the values I would like to save in an excel file each time I click.
I got them by clicking respectively on class1, class2, class3 and class4
enter image description here
I am a beginner, any help is appreciated!
Thank you very much.
I just completed the previous program
def affiche ():
val = v.get()
print(val)
workbook = xlsxwriter.Workbook('Mon fichier.xlsx')
worksheet = workbook.add_worksheet("My sheet")
data = [
['class1', val],
['class2', val],
['class3', val],
['class4', val],
]
caption = 'My data.'
# Set the columns widths.
worksheet.set_column('B:G', 12)
# Write the caption.
worksheet.write('B1', caption)
# Add a table to the worksheet.
worksheet.add_table('B4:L4', {'header_row': 0})
# Table data can also be written separately, as an array or individual cells.
worksheet.write_row('B4', data[0])
worksheet.write_row('B5', data[1])
worksheet.write_row('B6', data[2])
worksheet.write_row('B7', data[3])
workbook.close()

How to prevent global variables in Tkinter?

As stated in the title, how do I prevent the use of global variables in Tkinter? I have started to learn Tkinter and am running into a problem in almost all of my projects. The problem I run into is that most Widgets need to interact with each other creating a lot of dependencies. Below is the code from one of my projects that has this problem. I included all the code from this project because the problem arises throughout the code.
As an example consider the apply_filter function. In order to receive the input from the user, the Apply Filter button's command function needs access to the entry fields. One way this can be solved is by passing all the entry fields to the command function as arguments. However, I don't like this option because this means that I either have to place all the code into on giant block making the code more difficult to read or I have to pass it through multiple functions which leads to the same result. The option I chose here was to make the inputs global so that I can access them from anywhere within my code. At least the code is easier to read then. Of course this is probably not a good solution. When the GUI scales up this will create a giant mess of dependencies that could easily lead to bugs.
Long story short, I want to know: Is there a better way to deal with these dependencies so that I don't need to create tons of global variables or create unreadable code? Thanks in advance for any effort taken.
import tkinter as tk
from tkinter import filedialog as fd
import pandas as pd
import numpy as np
import sys
start_filter_row = 0
start_filter_column = 0
data_location = ""
df = None
root = None
filterr = {"customercode":{"column name": "Customer (code)", "value": None, "row_number": start_filter_row},
"partnr":{"column name": "Part nr", "value": None, "row_number": start_filter_row + 1},
"partclass":{"column name": "Part class", "value": None, "row_number": start_filter_row + 2},
"partfamily":{"column name": "Part familiy", "value": None, "row_number": start_filter_row + 3},
"startdate":{"column name": "Transaction date", "value": None, "row_number": start_filter_row + 4},
"enddate":{"column name": "Transaction date", "value": None, "row_number": start_filter_row + 5}
}
inputs = dict()
'''
Load Data
'''
def load_dataset():
global df
root.update()
database_location = fd.askopenfilename()
df = load_data(database_location)
def load_data(database_location):
try:
df = pd.read_pickle(database_location)
return df
except Exception as e:
print("Data location not set!", e)
root.destroy()
sys.exit()
'''
create Widgets
'''
def create_filter_inputs():
for key, value in filterr.items():
label = tk.Label(root, text = key)
label.grid(row = value["row_number"], column = start_filter_column, sticky = "w")
entry = tk.Entry(root)
entry.focus_set()
entry.grid(row = value["row_number"], column = start_filter_column + 1, sticky = "w")
inputs[key] = entry
def create_filter_buttons():
apply_button = tk.Button(root, text = "Apply Filter", command = apply_filter)
apply_button.grid(row = start_filter_row + len(filterr), column = 0, sticky = "news")
clear_button = tk.Button(root, text = "Clear Filter", command = clear_filter)
clear_button.grid(row = start_filter_row + len(filterr), column = 1, sticky = "news")
'''
Button functions
'''
def clear_filter():
for key, entry in inputs.items():
entry.delete(0, 'end')
apply_filter()
def apply_filter():
for key, entry in inputs.items():
filterr[key]["value"] = entry.get().split(";")
def main():
global data_location, root
'''
Setting up Tkinter Frame and loading dataset
'''
root = tk.Tk()
root.title("Data Tool V0.1")
loading_label = tk.Label(root, text = "Loading...", width = 30, height = 5)
loading_label.pack()
load_dataset()
loading_label.destroy()
n_rows, n_cols = (4, 3)
root.columnconfigure(tuple(range(n_cols)), weight=1)
root.rowconfigure(tuple(range(n_rows)), weight=1)
'''
Setting up GUI
'''
create_filter_inputs()
create_filter_buttons()
root.mainloop()
if __name__ == "__main__":
main()
You can avoid using globals by converting your GUI into a class format and making them class variables instead.
class GUI:
def __init__():
.....
self.myvariable = ...
def function():
newvar = self.myvariable - 5
Its also worth noting that variables defined outside of functions can be seen from within functions, but not vice versa:
>>> def f(a):
... print(a)
...
>>> b=2
>>> f(b)
2

How to create checkboxes from a list

I'm trying to learn to code with Python. I'm trying to create checkbuttons from a list by using a for loop. After the user checks the appropriate boxes, I'd like to end up with a subset of the original list containing only the values corresponding to checked boxes. I've gotten the checkbuttons to show ok, but what do I need to do to get the values of the checkboxes into a list? I've looked at several examples but I may not have understood correctly since I'm still learning all of this. Thanks for the help.
category_data = ['Home', 'Auto', 'Groceries','Medical']
for category in category_data:
l=Checkbutton(root, text= category,onvalue = True, offvalue= False)
l.pack(anchor=W)
First you need to assign a BooleanVar to each Checkbox in order to get its status. Then you need to use a dict to store those BooleanVar using category as the key.
Later you can go through the dict to create the required list of checked categories.
from tkinter import *
root = Tk()
category_data = ['Home', 'Auto', 'Groceries', 'Medical']
cb_vars = {} # dict to store the BooleanVar
for category in category_data:
var = BooleanVar()
l = Checkbutton(root, text=category, variable=var, onvalue=True, offvalue=False)
l.pack(anchor=W)
cb_vars[category] = var # store the BooleanVar
def show_selected_categories():
selected_category = [c for c in cb_vars if cb_vars[c].get()]
print(selected_category)
Button(root, text="Check", command=show_selected_categories).pack()
root.mainloop()
you can create a list, category_values, and append tk.BooleanVar objects to it that are used for the checkbuttons.
category_data = ['Home', 'Auto', 'Groceries','Medical']
category_values = []
for category in category_data:
category_values.append(BooleanVar())
l=Checkbutton(root, text= category, variable=category_values[-1], onvalue = True, offvalue= False)
l.pack(anchor=W)
and to get the value:
category_values[<category number>].get()

Tkinter labels not returning the proper value

Using python 2.7 and Tkinter.
I am creating four lables in a loop and binding them to . I want the label to return
the name in the label's text.
The problem is that no matter which label I press it returns the name in the last label.
I found this question Python Tkinter: Bind function with labels in for loop with exactly my problem but the solution given does not work for me even if I copied the code exactly.
Please anyone? here is my original code:
# labelbind.py
from Tkinter import *
root = Tk()
root.title('Label choices')
root.geometry('1160x900+650+50')
root.option_readfile('setstyle2.txt')
def lblpress(x):
print 'Label pressed', x
names = ['AMEX', 'CIBC', 'VISA', 'BMO']
col = 150
row = 45
num = 1
for name in names:
bobo = 'lbl' + str(num)
print bobo, name
bobo = Label(root, text = name)
bobo.bind('<ButtonRelease-1>', lambda x = name : lblpress(name))
bobo.place(x = col, y = row)
row += 40
num += 1
root.mainloop()
You don't need to pass anything to the callback. The event object that is given to the callback contains a reference to the widget, and from the widget you can get the text.
For example:
import Tkinter as tk
def lblpress(event):
print 'Label pressed:', event.widget.cget("text")
root = tk.Tk()
names = ['AMEX', 'CIBC', 'VISA', 'BMO']
for name in names:
label = tk.Label(root, text=name)
label.bind("<ButtonRelease-1>", lblpress)
label.pack(side="top")
root.mainloop()

Python Tkinter Treeview - Iterating 'get_children' output

I am trying to later iterate through the data inside of the Treeview. I will then hope to be able to sort through it.
from tkinter import *
from tkinter.ttk import *
import pickle
root = Tk()
def treeData(event):
children = tree.get_children()
print(children)
entry = StringVar()
a = Entry(root, textvariable=entry)
a.grid(column=0,row=0)
a.bind("<Key>", function)
file_data = []
file = open('data.dat', 'rb')
while True:
try:
file_data.append(pickle.load(file))
except EOFError:
break
file.close()
column_names = ("Column 1", "Column 2")
tree = Treeview(root, columns=column_names)
tree['show'] = 'headings'
for x in file_data:
a = tree.insert('', 'end', values=x)
for col in column_names:
tree.heading(col, text=col)
tree.grid(column=0, row=1)
In the function, called 'treeData' when I print(children) it outputs a list that looks similar to this - ('I001', 'I002', 'I003', 'I004')
I am hoping someone will know how to convert these pieces of data into what is actually shown in the row of the Treeview?
Thanks,
What you are asking is documented in the official tkinter documentation for the Treeview widget.
The get_children method returns a list of item IDs, one for each child. The item method of the treeview will return a dictionary of data for a given item. Thus, you can iterate over the values with something like this:
for child in tree.get_children():
print(tree.item(child)["values"])

Categories

Resources