Seperating two functions using Treeview - python

I am trying to bind two different functions to two different parts of the treeview. the command deleteItem should only apply to rows, whilst the columnselect function should only apply to columns. When I run this code the second bind overrides the first.
import tkinter as tk
from tkinter import messagebox
from tkinter.font import Font
from tkinter import scrolledtext
from tkinter import ttk
def deleteItem(a):
print("In delete Item")
curItem = AttendanceView.focus()
inter_var = AttendanceView.item(curItem)
ListValues = inter_var['values']
print(ListValues)
def columnnameselect(q):
tk.messagebox.showinfo("","You didn't press a row")
# AttendanceView Specific Table Components
Attendance=tk.Tk()
AttendanceView = ttk.Treeview(Attendance)
AttendanceView["columns"] = ("firstname", "secondname","patrol","present")
AttendanceView.grid(row=3, column=1)
AttendanceView.heading("#0", text="", anchor="w")
AttendanceView.column("#0", anchor="center", width=5, stretch=tk.NO)
AttendanceView.heading("firstname", text="First Name", anchor="w")
AttendanceView.column("firstname", anchor="center", width=80)
AttendanceView.heading("secondname", text="Second Name", anchor="w")
AttendanceView.column("secondname", anchor="center", width=90)
AttendanceView.heading("patrol", text="Patrol", anchor="w")
AttendanceView.column("patrol", anchor="center", width=80)
AttendanceView.heading("present", text="Present", anchor="w")
AttendanceView.column("present", anchor="center", width=80)
AttendanceView.grid(row=3, column=1, columnspan=5)
AttendanceView.bind('<ButtonRelease-1>', deleteItem)
AttendanceView.bind('<ButtonRelease-1>', columnnameselect)
AttendanceViewScrollbar = ttk.Scrollbar(Attendance, orient="vertical", command=AttendanceView.yview)
AttendanceView.configure(yscroll=AttendanceViewScrollbar.set)
AttendanceViewScrollbar.grid(row=3, column=6, sticky="ns")
AttendanceView.insert("", "end", text="", values=(("Ethel",("Kennedy"),("Cow"),(""))))
AttendanceView.insert("", "end", text="", values=(("Jack",("Kennedy"),("Lion"),(""))))
AttendanceView.insert("", "end", text="", values=(("Bobby",("Kennedy"),("Zebra"),(""))))
The desired outcome:
When a column is selected, columnselect is run.
When a row is selected, deleteItem is run.
Any help would be appreciated.

Related

How to set default value of radio button using TKinter in a class?

I'm trying to set the default value of a radio button using TKinter and Python. It's my first time using it so I'm pretty new. My understanding is that the default value should be set to the second radio button in my example (value=1).
from tkinter import *
from tkinter import ttk
class RadioButtons:
def __init__(self, root):
self.root = root
self.jobNum = IntVar(value=1)
self.create()
def create(self):
content = ttk.Frame(self.root)
radioButtons = ttk.LabelFrame(content, borderwidth=5, relief="ridge", width=400, height=400, text="Radio Buttons")
radioButtonsLbl=ttk.Label(radioButtons, text="Buttons")
# radio buttons
jobType1 = ttk.Radiobutton(radioButtons, text="Button 0", variable= self.jobNum, value=0)
jobType2 = ttk.Radiobutton(radioButtons, text="Button 1", variable= self.jobNum, value=1)
jobType3 = ttk.Radiobutton(radioButtons, text="Button 2", variable= self.jobNum, value=2)
content.grid(column=0, row=0)
# add to grid
radioButtons.grid(column=0, row=0, columnspan=3, rowspan=3)
radioButtonsLbl.grid(column=0, row=5, padx=20, pady=5, sticky=W)
jobType1.grid(column=1, row=5, padx=20, pady=0, sticky=W)
jobType2.grid(column=1, row=6, padx=20, pady=0, sticky=W)
jobType3.grid(column=1, row=7, padx=20, pady=0, sticky=W)
root = Tk()
RadioButtons(root)
root.mainloop()
However no radio button is selected when running the program. (screenshot of program)
The debugger confirms that the value of self.jobNum is set correctly.(screenshot of debugger)
How do I set the default value? I've tried a number of things including self.jobNum.set() before and after creating and adding the radio buttons but to no avail.
What am I missing here? Is this some kind of scope issue?
I suspect this has something to do with python's garbage collector. I can make the problem go away by saving a reference to RadioButtons(root):
root = Tk()
rb = RadioButtons(root)
root.mainloop()

How to Get() values from a list of entry widgets using Tkinter in Python

I'm using a button to add a new entry widget every time it is pressed, which works. However, whenever I itierate over the list to get the values in the entry widgets, I get an error:
AttributeError: 'list' object has no attribute 'get'
What am I missing? I found similar questions but the answers arent' working for me.
Steps to reproduce:
Run this code
Click "Add system block" to add Entry widgets
Type something into entry blocks
Click "Get Entry Values"
Expected Results: Whatever was typed in entry blocks are printed to console
from tkinter import *
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import(FigureCanvasTkAgg, NavigationToolbar2Tk)
import numpy as np
class MyApp:
def __init__(self, parent):
self.myParent = parent ###remember my parent, the root
parent.geometry("500x300+0+0") #Default window size
global system_block_list, num_of_block
system_block_list = []
num_of_block = 0
self.frm_detail=tk.Frame(
master=root,
relief=tk.RAISED,
borderwidth=1,
height=100,
bg="#e0e0e0"
)
self.frm_detail.rowconfigure([0,1], weight=0)
self.frm_detail.columnconfigure(0, weight=1, minsize=50)
self.frm_detail.columnconfigure(1, weight=10, minsize=50)
self.frm_detail.grid(row=0,column=0, sticky="nsew")
lbl_detail = tk.Label(master=self.frm_detail, text="System Diagram", bg="white", fg="black")
lbl_detail.grid(row=0,column=0, columnspan=2, sticky="new")
"""""Diagram Buttons Frame"""""
self.frm_diagram_btns=tk.Frame(
master=self.frm_detail,
relief=tk.RAISED,
borderwidth=1,
height=300,
bg="#e0e0e0"
)
self.frm_diagram_btns.grid(row=1,column=0, rowspan=1, sticky="nsew")
self.btn_add_block = tk.Button(
text="Add System Block",
width=20,
height=1,
bg="#c4fffe",
fg="black",
master=self.frm_diagram_btns,
relief=RAISED
)
self.btn_add_block.pack()
self.btn_add_block.pack_propagate(0)
self.btn_add_block.bind("<Button-1>", self.handle_add_block)
self.btn_get_values = tk.Button(
text="Get Entry Values",
width=20,
height=1,
bg="#c4fffe",
fg="black",
master=self.frm_diagram_btns,
relief=RAISED
)
self.btn_get_values.pack()
self.btn_get_values.pack_propagate(0)
self.btn_get_values.bind("<Button-1>", self.handle_button1)
"""""Block Diagram Frame"""""
self.frm_block_diagram=tk.Frame(
master=self.frm_detail,
relief=tk.RAISED,
borderwidth=1,
height=300,
bg="#e0e0e0"
)
self.frm_block_diagram.grid(row=1,column=1, rowspan=1, sticky="nsew")
'''TEST STARTS HERE'''
def handle_button1(self, event):
#Get value in filename entry
global system_block_list
values = [int(entry.get()) for entry in system_block_list]
print(values)
return values
def handle_add_block(self, event) :
global system_block_list, num_of_block
'''Generate a new Entry Widget and use grid to place it in frm_block_diagram'''
system_block_list.append([tk.Entry(self.frm_block_diagram, relief="raised",width=10,bg="white",fg="black")])
system_block_list[-1][0].grid(row=0, column=num_of_block, sticky='nsew', ipadx=5, ipady=10, padx=10, pady=10)
system_block_list[-1][0].grid_propagate(0)
'''reconfigure frm_block_diagram for another column'''
self.frm_block_diagram.columnconfigure(num_of_block, weight=1)
num_of_block += 1
return num_of_block
#Run the event loop
root = tk.Tk()
myapp = MyApp(root)
root.mainloop()
As pointed out by jasonharper. system_block_list is not a list of entries, but a list of lists of entries. Therefore changing:
system_block_list.append([tk.Entry(self.frm_block_diagram, relief="raised",width=10,bg="white",fg="black")])
system_block_list[-1][0].grid(row=0, column=num_of_block, sticky='nsew', ipadx=5, ipady=10, padx=10, pady=10)
system_block_list[-1][0].grid_propagate(0)
to
entry = tk.Entry(self.frm_block_diagram, relief="raised",width=20,bg="white",fg="black")
system_block_list.append(entry)
system_block_list[-1].grid(row=0, column=num_of_block, sticky='nsew', ipadx=5, ipady=10, padx=10, pady=10)
system_block_list[-1].grid_propagate(0)
All better!

Nested layouts with Tkinter

The Tk documentation says (last section) that nested layouts can be achieved using tk.frame.
The following small example is not working as expected, instead of:
import tkinter as tk
window = tk.Tk()
window.geometry('250x100')
# first level, window as parent
tk.Label(window, text='Choose file:').grid(row=0, column=0, sticky=tk.W)
tk.Button(window, text='Browse ...').grid(row=1, column=0, sticky=tk.W)
fr = tk.Frame(window).grid(row=2, column=0, sticky=tk.W)
# nested, frame as parent
tk.Entry(fr).grid(row=0, column=0, sticky=tk.W)
tk.Entry(fr).grid(row=0, column=1, sticky=tk.W)
tk.mainloop()
it produces:
The real UI is much more complex, so I really want to use nested grids instead of one grid with multiple columns.
AFAIK, tkinter does not produce intuitive results if you create an object and grid at once. This should give you the same result with the documentation:
import tkinter as tk
window = tk.Tk()
window.geometry('250x100')
# first level, window as parent
tk.Button(window, text='Browse ...').grid(row=1, column=0, sticky=tk.W)
tk.Label(window, text='Choose file:').grid(row=0, column=0, sticky=tk.W)
fr = tk.Frame(window)
fr.grid(row=2, column=0, sticky=tk.W)
# nested, frame as parent
entry1 = tk.Entry(fr)
entry1.grid(row=0, column=0, sticky=tk.W)
entry2 = tk.Entry(fr)
entry2.grid(row=0, column=1, sticky=tk.W)
tk.mainloop()

Is it possible to make 'dynamically' adjustable widgets in Tkinter/ttk

I'm developing very simple GUI for my DB. It shows record's list/tree in DB on left panel and (if user clicks on some record) shows the record on the right panel.
Here some bit of code which creates GUI
from Tkinter import *
import ttk
master = Tk()
reclist = ttk.Treeview(columns=["TIME STAMP","HASH","MESSAGE"])
ysb = ttk.Scrollbar(orient=VERTICAL, command= reclist.yview)
xsb = ttk.Scrollbar(orient=HORIZONTAL, command= reclist.xview)
reclist['yscroll'] = ysb.set
reclist['xscroll'] = xsb.set
reclist.grid(in_=master, row=0, column=0, sticky=NSEW)
ysb.grid(in_=master, row=0, column=1, sticky=NS)
xsb.grid(in_=master, row=1, column=0, sticky=EW)
Comment = Text(master)
Comment.tag_configure("center", justify='center')
ysc = ttk.Scrollbar(orient=VERTICAL, command= Comment.yview)
xsc = ttk.Scrollbar(orient=HORIZONTAL, command= Comment.xview)
Comment.grid(in_=master,row=0,column=2,sticky=W+E+N+S)#, columnspan=5)
ysc.grid(in_=master, row=0, column=3, sticky=NS)
xsc.grid(in_=master, row=1, column=2, sticky=EW)
master.rowconfigure(0, weight=3)
master.columnconfigure(0, weight=3)
master.columnconfigure(2, weight=3)
master.mainloop()
Everything works pretty well, except that two panels are not adjustable. I cannot move border between them to make list of records or record panel bigger or smaller. I'm pretty sure in is possible (for example in gitk you can move the border between the list of commits and a displaied commit). I've search quite a lot with no luck.
What you are looking for is called a "PanedWindow". Both the tkinter and ttk modules have one, and they work almost identically. The general idea is that you create a PanedWindow instance, and then you add two or more widgets to it. The PanedWindow will add a movable slider between each widget. Typically you would use frames, which you can then fill up with other widgets.
Here is an example using the one in Tkinter:
import Tkinter as tk
root = tk.Tk()
pw = tk.PanedWindow()
pw.pack(fill="both", expand=True)
f1 = tk.Frame(width=200, height=200, background="bisque")
f2 = tk.Frame(width=200, height=200, background="pink")
pw.add(f1)
pw.add(f2)
# adding some widgets to the left...
text = tk.Text(f1, height=20, width=20, wrap="none")
ysb = tk.Scrollbar(f1, orient="vertical", command=text.yview)
xsb = tk.Scrollbar(f1, orient="horizontal", command=text.xview)
text.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set)
f1.grid_rowconfigure(0, weight=1)
f1.grid_columnconfigure(0, weight=1)
xsb.grid(row=1, column=0, sticky="ew")
ysb.grid(row=0, column=1, sticky="ns")
text.grid(row=0, column=0, sticky="nsew")
# and to the right...
b1 = tk.Button(f2, text="Click me!")
s1 = tk.Scale(f2, from_=1, to=20, orient="horizontal")
b1.pack(side="top", fill="x")
s1.pack(side="top", fill="x")
root.mainloop()

How to change the color of a Tkinter label programmatically?

I am trying to change the color of a Tkinter label when ever the user clicks the check button. I am having trouble writing the function correctly and connecting that to the command parameter.
Here is my code:
import Tkinter as tk
root = tk.Tk()
app = tk.Frame(root)
app.pack()
label = tk.Label(app, bg="white", pady=5, font=(None, 1), height=20, width=720)
checkbox = tk.Checkbutton(app, bg="white", command=DarkenLabel)
label.grid(row=0, column=0, sticky="ew")
checkbox.grid(row=0, column=0, sticky="w")
def DarkenLabel():
label.config(bg="gray")
root.mainloop()
Thank you
In your code, command=DarkenLabel is unable to find reference to the function DarkenLabel. Thus you need to define the function above that line, so you may use your code as following:
import Tkinter as tk
def DarkenLabel():
label.config(bg="gray")
root = tk.Tk()
app = tk.Frame(root)
app.pack()
label = tk.Label(app, bg="white", pady=5, font=(None, 1), height=20, width=720)
checkbox = tk.Checkbutton(app, bg="white", command=DarkenLabel)
label.grid(row=0, column=0, sticky="ew")
checkbox.grid(row=0, column=0, sticky="w")
root.mainloop()
Hope it helps!

Categories