Display labels using loop on button press - python

So I'm reading contents from the database and I have appended necessary elements into lists. I am trying to display each elements in the list in a label. For example, I have a list from the database:
test = ['Name' , 'Age', 'Location']
I want to display the label with text- Name (i.e test[0]) on button press. Likewise, when I press the button again I want the label to display, Age (i.e. test[1]) and so on upto the length of the list. What I tried:
from tkinter import *
import sqlite3
# connecting to the database
conn = sqlite3.connect('database.db')
c = conn.cursor()
# empty lists to later append elements into
all_ids = []
all_names = []
all_time = []
# execute sql
sql = ('SELECT * FROM appointments')
res = c.execute(sql)
for r in res:
idd = r[0]
name = r[1]
time = r[6]
all_ids.append(idd)
all_names.append(name)
all_time.append(time)
# variable that determines the index of elements on button press (updates)
x = 0
class Application:
def __init__(self, master):
self.master = master
self.heading = Label(master, text="Appointments", font=('arial 60 bold'), fg='steelblue')
self.heading.place(x=300, y=0)
self.btn = Button(master, text="Next Patient", width=20, height=2, command=self.func)
self.btn.place(x=500, y=600)
self.display = Label(self.master, text="", font=('arial 200 bold'))
self.display.place(x=500, y=80)
self.nd = Label(self.master, text="", font=('arial 80 bold'))
self.nd.place(x=200, y=400)
def func(self):
global x
for i in all_ids:
self.display.config(text=str(all_ids[x]))
self.nd.config(text=str(all_names[x]))
x += 1
root = Tk()
v = Application(root)
root.geometry("1366x768+0+0")
root.resizable(False, False)
root.mainloop()
When I press the button, last element is displayed and when I press again, I get list index out of range. Can anyone please tell me what I'm doing wrong? Thanks
Output is the label of last element in the list and error on clicking again:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.5/tkinter/__init__.py", line 1553, in __call__
return self.func(*args)
File "test.py", line 45, in func
self.display.config(text=str(all_ids[x]))
IndexError: list index out of range

for loop in func seems unnecessary since you are saying you want to show one of your labels on each press. Remove it.
Also you can move x inside of your class if you are only using it in there.
def __init__(self, master):
...
self.x = 0
def func(self):
self.display.config(text=str(all_ids[self.x]))
self.nd.config(text=str(all_names[self.x]))
self.x += 1

Related

tkinter how delete both checkbox and label if checked box

Im trying to check a checkbox and delete both 'checked box' and 'label' with upper button. They are both created in a loop from the same list.
I've tried to build a dictionary with 'buttons' as key and 'result_of_checkboxes' as value and destroy key if value is different from ''. It doesnot work. If buttons are keys, why can't I destroy them? What is the correct approach?
Thanks in advance!
from tkinter import *
root = Tk()
root.geometry('400x400')
def destroy_button():
for key, value in dict_check_and_buttons:
if value != '' and current_var != '':
key.destroy()
current_box.destroy()
my_friends = ['Donald', 'Daisy', 'Uncle Scrooge']
button1= tk.Button(root, text = "delete both if cbox is checked", command = destroy_button).pack()
#-----ild checkbuttons and store results in checkbutton_result list
checkbutton_result = []
for index, friend in enumerate(my_friends):
current_var = tk.StringVar()
current_box = tk.Checkbutton(root, text= friend,
variable = current_var,
onvalue = friend, offvalue = ""
)
checkbutton_result.append(current_var) #append on and off results
current_box.pack()
#-----build buttons and store them in buttons_list
buttons_list = []
for index, friend in enumerate(my_friends):
buttons= tk.Button(root, text = friend).pack()
buttons_list.append(buttons)
#-----build a dict with list to say "if onvalue != '' destroy button"
dict_check_and_buttons = dict(zip(buttons_list, checkbutton_result))
root.mainloop()
the error is:
Exception in Tkinter callback
Traceback (most recent call last):
File "c:\python\python38\lib\tkinter\__init__.py", line 1892, in __call__
return self.func(*args)
File "<ipython-input-18-954d3a090f2c>", line 7, in destroy_button
for key, value in dict_check_and_buttons:
TypeError: cannot unpack non-iterable NoneType object
There are following issues in your code:
all items in buttons_list are None due to the following line:
buttons= tk.Button(root, text = friend).pack() # buttons is the result of pack()
The line should be split into two lines:
buttons = tk.Button(root, text=friend)
buttons.pack()
you cannot get a (key, value) pair by iterating a dictionary:
for key, value in dict_check_and_buttons: # dict_check_and_buttons is a dictionary
...
Instead you should iterate on the result of dict_check_and_buttons.items():
for key, value in dict_check_and_buttons.items():
...
you need to call get() on a tk.StringVar():
for key, value in dict_check_and_buttons.items():
if value.get() != '': # use value.get()
key.destroy()
If you need to destroy the checkbutton as well, you need to save the checkbutton to checkbutton_result along with its associated variable:
checkbutton_result = []
for index, friend in enumerate(my_friends):
current_var = tk.StringVar()
current_box = tk.Checkbutton(root, text= friend,
variable = current_var,
onvalue = friend, offvalue = ""
)
checkbutton_result.append((current_box, current_var)) # save both checkbutton and its associated variable
current_box.pack()
Then destroy the checkbutton inside destroy_button():
def destroy_button():
for btn, (cb, cbvar) in dict_check_and_buttons.items():
if cbvar.get() != '':
btn.destroy()
cb.destroy()
With your answer I rebuild the code. It is perfect now thanks to you.
import tkinter as tk
root = tk.Tk()
root.geometry('400x400')
'''
to get if checkbox is checked to use results to delete checkbox and label
'''
def destroy_button_box():
for button_names, (ckbox, on_off) in dict_buttons_box_var.items():
if on_off.get() != 0:
button_names.destroy()
ckbox.destroy()
my_friends = ['Donald', 'Daisy', 'Uncle Scrooge']
button1 = tk.Button(root, text="delete label if box is checked", command=destroy_button_box).pack()
checkbox_variable_list = []
box_list = []
for index, friend in enumerate(my_friends):
# current_var = tk.IntVar()
current_var = tk.IntVar()
# current_var = tk.StringVar()
current_box = tk.Checkbutton(root, text=friend,
variable=current_var,
onvalue=1, offvalue=0
)
current_box.pack()
# append checkbox and 'on' or 'off' results
checkbox_variable_list.append((current_box, current_var))
# -----build buttons and store them in buttons_list
buttons_list = []
for index, friend in enumerate(my_friends):
button_names = tk.Button(root, text=friend)
button_names.pack()
# append buttons in loop
buttons_list.append(button_names)
# -----build a dict with lists to use in destroy_box_button function:
dict_buttons_box_var = dict(zip(buttons_list, checkbox_variable_list))
root.mainloop()

ttk.treeview selection_set to the item with specific id

I want to edit an item in treeview by another Toplevel window and after editing want to refresh / reload items into list from database and set focus to the edited item.
The problem I am facing is to SET FOCUS TO THE EDITED ITEM IN TREEVIEW. Any help will be appreciated.
Here is the minimal sample code.
import tkinter as tk
from tkinter import ttk
class _tree(tk.Frame):
def __init__(self, *args):
tk.Frame.__init__(self, *args)
self.tree = ttk.Treeview(self, columns = ("id", "name"))
self.tree.heading("#0", text = "s.n")
self.tree.heading("#1", text = "id")
self.tree.heading("#2", text = "name")
self.tree.pack()
_items = [[52,"orange"],[61,"manggo"],[1437,"apple"]] # item with id 61 needs to be changed
sn = 1
for r in (_items):
self.tree.insert("", "end", text = str(sn), values = (r[0], r[1]))
sn += 1
self.tree.bind("<Double-Button-1>", self._item)
def _item(self, event):
global item_values
global item_id
global item_name
idx = self.tree.selection()
item_values = self.tree.item(idx)
print("item_values : %s" % item_values)
item_id = self.tree.set(idx, "#1")
item_name = self.tree.set(idx, "#2")
edit_item(self)
class edit_item(tk.Toplevel):
def __init__(self, master, *args):
tk.Toplevel.__init__(self, master)
self.master = master
global item_values
global item_name
lbl1 = tk.Label(self, text = "item name")
self.ent1 = tk.Entry(self)
btn1 = tk.Button(self, text = "update", command = self.update_item)
lbl1.place(x=0, y=10)
self.ent1.place(x=90, y=10)
btn1.place(x=90, y=100)
self.ent1.insert(0, item_name)
def update_item(self):
for i in self.master.tree.get_children():
self.master.tree.delete(i)
new_data = [[52,"orange"],[61,"mango"],[1437,"apple"]] # item with id 61 has been changed
sn = 1
for r in (new_data):
self.master.tree.insert("", "end", text = str(sn), values = (r[0], r[1]))
sn += 1
# Need to set focus on item with id 61
idx = self.master.tree.get_children(item_values['values'][0]) # HERE NEED HELP
self.master.tree.focus_set()
self.master.tree.selection_set(idx)
self.master.tree.focus(idx)
self.destroy()
def main():
root = tk.Tk()
app = _tree()
app.pack()
root.mainloop()
if __name__ == "__main__":
main()
`
I am receiving the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.8/tkinter/__init__.py", line 1883, in __call__
return self.func(*args)
File "test_tree.py", line 55, in update_item
idx = self.master.tree.get_children(item_values['values'][0]) # HERE NEED HELP
File "/usr/lib/python3.8/tkinter/ttk.py", line 1225, in get_children
self.tk.call(self._w, "children", item or '') or ())
_tkinter.TclError: Item 61 not found
You don't need to delete the existing values in order to update the value. You can simply use the set() method to update your treeview.
syntax:
tree.set(iid, column=None, value=None)
If you specify only iid in set method it will return items as dict.
Here is a better way to do the same.
from tkinter import ttk
import tkinter as tk
titles={'Id': [1,2,3,4,5, 6, 7, 8, 9], 'Names':['Tom', 'Rob', 'Tim', 'Jim', 'Kim', 'Kim', 'Kim', 'Kim', 'Kim']}
def update(selected_index_iid, changed):
index = treev.index(selected_index_iid)# or just index = treev.index(treev.selection())
treev.set(selected_index_iid, 1, changed) # updating the tree
titles['Names'][index] = changed #updating the dictionary
print(titles)
def clicked(event):
global titles
top = tk.Toplevel(window)
label = tk.Label(top, text='Update: ')
label.pack()
entry = tk.Entry(top)
entry.insert(0, treev.set(treev.selection())['1']) #if you only specify the iid 'set' will return dict of items, ['1'] is to select 1st column
entry.pack()
button= tk.Button(top, text='Update', command=lambda :update(treev.selection(), entry.get()))
button.pack()
window = tk.Tk()
treev = ttk.Treeview(window, selectmode ='browse')
treev.bind('<Double-Button-1>', clicked)
treev.pack(side='left',expand=True, fill='both')
verscrlbar = ttk.Scrollbar(window,
orient ="vertical",
command = treev.yview)
verscrlbar.pack(side ='right', fill ='y')
treev.configure(yscrollcommand = verscrlbar.set)
treev["columns"] = list(x for x in range(len(list(titles.keys()))))
treev['show'] = 'headings'
for x, y in enumerate(titles.keys()):
treev.column(x, minwidth=20, stretch=True, anchor='c')
treev.heading(x, text=y)
for args in zip(*list(titles.values())):
treev.insert("", 'end', values =args)
window.mainloop()

Need a way to make a button in Tkinter become DISABLED when nothing is highlighted in a Listbox

The aim of the code I am writing is for it to be able to remove items from an array and a corresponding listbox. I hope to be able to have the button that removes the items be DISABLED when no item is highlighted in the listbox (because otherwise it returns an error when you try to press the button and nothing has been selected, error is shown below.)
>>> Exception in Tkinter callback
Traceback (most recent call last):
File "G:\2Boys_stuff\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "H:/The Quiet Apocalypse/Tests/test_13.py", line 14, in remove
LB = int(lb[0])
IndexError: tuple index out of range
The code I am using is below:
from tkinter import *
import tkinter.messagebox as box
global listbox
TF = True
inventorylist1 = [("Item1","1.0"),("Item2","0.25"),("Item3","0.25")]
def remove():
global TF
global listbox
lb = listbox.curselection()
LB = int(lb[0])
del inventorylist1[LB]
TF = False
I(TF)
def IR():
global windowir
global listbox
windowir = Tk()
windowir.title( "IR" )
windowir.resizable( 0, 0 )
listframe = Frame( windowir )
listbox = Listbox( listframe )
for i in range(len(inventorylist1)):
e = i+1
listbox.insert(e, inventorylist1[i])
Label_ir = Label( listframe, relief = "groove" )
Label_ir.pack( side = TOP )
btn_ir_1 = Button( listframe, text = "Remove", command = remove )
btn_ir_1.pack(side = BOTTOM )
listbox.pack(side = BOTTOM)
listframe.pack(padx = 20, pady = 20)
Label_ir.configure( text = "You are carrying: " )
windowir.mainloop
def I(Tf):
global windowir
if Tf == True:
windowi = Tk()
windowi.title( "I" )
windowi.resizable( 0, 0 )
IR()
else:
windowir.destroy()
IR()
I(TF)
Just add try and except and show error as popup in except
def remove():
global TF
global listbox
try:
lb = listbox.curselection()
LB = int(lb[0])
del inventorylist1[LB]
TF = False
I(TF)
except:
popup = Tk()
popup.wm_title("!")
label = Label(popup, text=" ERROR")
label.pack(side="top", fill="x", pady=10)
B1 = Button(popup, text="Okay", command = popup.destroy)
B1.pack()
popup.mainloop()

How to use a IntVar to call an element in a 2d array

I am trying to make a game that generates frames with questions in them with the answers as buttons below them. I am nearly finished the base code, but I need a way to compare the answer the player clicks on and the correct answer. To do this, I put the right answer in the first element of the element that corresponds to the question asked. ie [[correct answer, wrong, wrong, wrong], [correct, wrong, wrong, wrong]].
I am having trouble using a IntVar to call the right set of answers and access the right element.
def check_answer(self, answer, page_name):
global answers
score = IntVar(self)
correct = IntVar(self)
if answer == answers[correct][0]:
s = score.get() + 1
score.set(s)
print(score)
frame = self.frames[page_name]
frame.tkraise()
correct = correct.get()+1
correct.set(correct)
When I press a button, I get this error message:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Chris\AppData\Local\Programs\Python\Python36-32\Lib\tkinter\__init__.py", line 1699, in __call__
return self.func(*args)
File "C:/Users/Chris/Desktop/Paul's stuff/Paul/CpmpProject/gamescreenv3.py", line 62, in <lambda>
answer = tkinter.Button(self, text=answers[0][i], command=lambda answer=i: controller.check_answer(answer, "QuestionTwo"))
File "C:/Users/Chris/Desktop/Paul's stuff/Paul/CpmpProject/gamescreenv3.py", line 44, in check_answer
if answer == answers[correct][0]:
TypeError: list indices must be integers or slices, not IntVar
I created a mini UI which will take user answer check against correct answer and if answer is correct it will add to score and display a pop up saying it was correct. Test it out and see if this is what you were looking for.
Copy and paste this into python and test it out:
from tkinter import *
import tkinter as tk
class Application(Frame):
def __init__ (self, master):
Frame.__init__(self,master)
self.grid()
self.answers=['a','b','c','d']
self.score = 0
q1 = Label(self, text ="What is the answer?")
q1.grid(row=0, column = 0)
a1 = Button(self, text = self.answers[0], command = lambda: self.checkAnswer(self.answers[0]))
a1.grid(row=1, column=0)
a2 = Button(self, text = self.answers[1], command = lambda: self.checkAnswer(self.answers[1]))
a2.grid(row=1, column=1)
a2 = Button(self, text = self.answers[2], command = lambda: self.checkAnswer(self.answers[2]))
a2.grid(row=2, column=0)
a2 = Button(self, text = self.answers[3], command = lambda: self.checkAnswer(self.answers[3]))
a2.grid(row=2, column=1)
def checkAnswer(self, answer):
if answer == self.answers[0]:
self.score += 1
self.correct()
else:
self.wrong()
def correct(self):
self.popup = tk.Tk()
self.popup.wm_title("Correct!!!")
label = tk.Label(self.popup,text=("Correct answer, your score is:",self.score))
label.pack()
self.popup.mainloop()
def wrong(self):
self.popup = tk.Tk()
self.popup.wm_title("wrong")
label = tk.Label(self.popup,text=("wrong answer"))
label.pack()
self.popup.mainloop()
root = tk.Tk()
app = Application(root)
root.mainloop()

trying to pass the current radiobutton value in tkinter but getting global name not defined

I have a list of radiobuttons and linked to the same variable and I have a submit button that when clicked I want it to pass the current variable value to another function which will then use that number. I used lambda function but when the function should be called I get global name 'num' is not defined. num is my function. Below is my code. Thank you very much.
from tkinter import *
import random
class App:
def __init__(self, master):
def say_one(self):
v = IntVar()
window = Toplevel(root)
for i in range(1,11):
self.label = Radiobutton(
window, text = i , fg = "red",
value = i, variable = v
).grid(row =i, column =0)
# error is occurring in the next line
self.report = Button(
window, text="submit", command= lambda : num (v)
).grid(row = 12, column = 0)
def num( number):
print(number)
root = Tk()
app = App(root)
root.mainloop()
Indent your code correctly.
from tkinter import *
import random
class App:
def __init__(self, master):
self.say_one()
def say_one(self):
v = IntVar()
window = Toplevel(root)
for i in range(1,11):
self.label = Radiobutton(
window, text = i , fg = "red",
value = i, variable = v
).grid(row =i, column =0)
# error is occurring in the next line
self.report = Button(
window, text="submit", command=lambda: num(v)
).grid(row = 12, column = 0)
def num(var):
print(var.get())
root = Tk()
app = App(root)
root.mainloop()
I fixed the indentation, changed the num function to print variable's value instead of printing variable itself.

Categories

Resources