I have frames in a grid with widgets in them. These frames get destroyed and new ones are created. I want to move between these with keyboard.
The Problem: every time there is a new frame it gets treated by focus next as the last one.
The Question: how to focus by row instead.
def create_a_line(i):
p=1 #sometimes there are more widgets in a row, but here just take 1
bframe = Frame(root)
bframe.grid(row=i, column=0)
m = Button(bframe, text=h[i], , takefocus = 1)
m.bind('<Right>', lambda event, i=i: focus_next(i, p))
m.bind('<Double-1>', lambda event, i=i: double_click_text(i))
m.grid(row=0, column=p)
def double_click_text(i)
for widget in root.grid_slaves(row=i):
widget.destroy()
#change h[i] i.e. text of the button to something else
create_a_line(i) #with new text but same row
def focus_next(i, p): #this is what im struggling with
event.widget.tk_focusNext().focus() #this is how it is right now
#this is how i would like it to be, but no idea how:
# if in row=i other widgets with column >p: sometimes there will be more than one widget in a row
# event.widget.tk_focusNext().focus()
# else:
# set_focus(first widget in grid row=i+1)
for i in range(0, 5) #the end number is based on number of lines f.e. 5
create_a_line(i)
For lack of a better solution this is how I solved it based on comments:
labellsall=[]
h = []
def ad_to_labelsall(i, p , z):
ag = ["#"+str(i)+"i#", p, z] #made i more complicated so I dont find random numbers
labelsall.append(ag)
def del_from_labelsall(i):
agi = ("#"+str(i)+"i#")
ngip = [e for e, s in enumerate(labelsall) if agi in s]
for index in sorted(ngip, reverse=True):
del labelsall[index]
def create_a_line(i):
p=1
bframe = Frame(root)
bframe.grid(row=i, column=0)
m = Button(bframe, text=h[i], , takefocus = 1)
m.bind('<Right>', lambda event, i=i: focus_next(event, i))
m.bind('<Double-1>', lambda event, i=i: double_click_text(i))
m.grid(row=0, column=p)
ad_to_labelsall(i, p , z=m)
def double_click_text(i)
del_from_labelsall(i)
for widget in root.grid_slaves(row=i):
widget.destroy()
#some other code change h[i] i.e. text of the button to something else
create_a_line(i) #with new text but same row
def focus_next(event, i):
grid_info = event.widget.grid_info()
p = grid_info["column"]
agi = ("#"+str(i)+"i#")
ngip = [e for e, s in enumerate(labelsall) if agi in s]
for index in sorted(ngip):
the_row = labelsall[index]
if int(the_row[1]) > p: #check if other widget is in the row before jumping to next row
the_row[2].focus_set() #if other widget found focus it
return("break")
else:
pass
i_next = i+1
try:
agi_next = ("#"+str(i_next)+"i#")
ngip_next = [e for e, s in enumerate(labelsall) if agi_next in s]
labelsall[ngip_next[0]][2].focus_set()
except:
agi_next = ("#"+str(i_next+1)+"i#") #sometimes there are rows with no widgets, so just try the next one
ngip_next = [e for e, s in enumerate(labelsall) if agi_next in s]
labelsall[ngip_next[0]][2].focus_set()
return("break")
How can I fetch values from each column of database separately and display each through label in python?
class display:
def __init__(self, master,ad):
self.master = master
self.master.title("STUDENT-INFO")
self.f = Frame(self.master,height = 1200, width = 1200)
self.f.propagate(0)
self.f.pack()
self.e1=ad.e1.get()
self.e2=ad.e2.get()
self.b1=Button(self.master,text="PRINT",width=15,command=self.print1)
self.b1.place(x=35,y=200)
self.exit = Button(self.f, text = 'EXIT', width = 15, command = self.exit_window)
self.exit.place(x=35,y=400)
def print1(self):
cursor.execute("select emp_name,pf,monthly_sal_inhand,bonus,yearly_sal_inhand from details6 where emp_id='{}' and password='{}'".format(self.e1,self.e2))
r=cursor.fetchall()
s=r[0]+""+r[1]+""+r[2]+""+r[3]
self.l3=Label(self.master,text='Format : Employee_name Monthly_pf Final_monthly_salary Yearly_Bonus Final_yearly_salary ',width=120)
self.l3.place(x=250,y=200)
self.l3=Label(self.master,text=s,width=80)
When I run this code I get an error message:
CONCATENATE ONLY TUPLE (NOT str) to tuple
You can in a better way print them in a list vertically by using the listWidget.
def print1(self):
list1 = []
cursor.execute("select emp_name,pf,monthly_sal_inhand,bonus,yearly_sal_inhand from
details6 where emp_id='{}' and password='{}'".format(self.e1,self.e2))
r=cursor.fetchall()
for i in range(len(r)):
list1.append(r[i][0])
#Now we have all the elements in list1 as a list
for in range(len(list1)):
item=list1[i]
self.listWidget.addItem(item)
I'm creating 3 groups of ttk.CheckButtons, using a class clsGrpCheckButton.
The problem is that I can access only the status of the last group created, and not for the previous group.
When clicking on one of the checkButtons of any group, I'm expecting to get the list of checkbuttons check in the group (by using method chkGrpGetValue with is the command parameter of each checkbutton)
However, whatever which button is clicked, the method only and always returns the status of the last group
Here is the code to recreate the issue, and in attachment a picture that shows the problems
Thks for your help.
Rgds
import tkinter as tk
from tkinter import ttk
import pandas as pd
class clsGrpCheckButton(ttk.Checkbutton):
def __init__(self,pContainer, pLstVal,pCommand,pInitValue=True):
self.grpChk=[0]*len(pLstVal)
self.grpKey=[0]*len(pLstVal)
self.grpLstVal = [0]*len(pLstVal)
self.grpVariable= [0]*len(pLstVal)
self.grpActiveKeys = [0]
for l,t in enumerate(pLstVal):
#l : index of the list of tuples
self.grpKey[l] = t[0]
self.grpLstVal[l] = t[1]
self.grpVariable[l] = tk.StringVar()
self.grpChk[l] = ttk.Checkbutton(pContainer, text=self.grpLstVal[l],
state='active' ,
onvalue= self.grpKey[l],
offvalue= '',
variable = self.grpVariable[l],
command=pCommand)
#get default value
if pInitValue :
self.grpVariable[l].set(self.grpKey[l])
self.grpActiveKeys.append(self.grpKey[l])
#get the index in the list of checkboxes
# depending on the key
def chkGrpGetIdx(self, pKey):
i=0
while i <len(self.grpKey):
if self.grpKey[i]==pKey :
return i
i=len(self.grpKey)
else:
i+=1
def chkGrpSetValue(self, pKey, pValue):
#need find correct index first
i=self.chkGrpGetIdx(pKey)
self.grpVariable[i] = pValue
#return the list of keys of the group
def chkGrpKeyLst(self):
return self.grpKey
#return the checkox element of the group of checkox
def chkGrpGetChkObj(self,pKey):
i=self.chkGrpGetIdx(pKey)
return self.grpChk[i]
#action when check/uncheck
#at list one element should be active
def chkGrpGetValue(self):
i=0
r=len(self.grpVariable)
self.grpActiveKeys.clear()
while i < len(self.grpVariable):
if self.grpVariable[i].get() =='':
r-=1
else:
self.grpActiveKeys.append(self.grpKey[i])
i+=1
if r==0:
self.grpVariable[0].set(self.grpKey[0])
self.grpActiveKeys.append(self.grpKey[0])
print(self.grpActiveKeys)
#to avoid accessing to class attribute directly
def chkGetCheckedValues(self):
return self.grpActiveKeys
class clsWindows(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
la = [1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3]
lb= [10,11,12,14,15,20,21,22,23,24,25,26,30,31,32,33]
lc=['d10','d11','d12','d14','d15','d20','d21','d22','d23','d24','d25','d26','d30','d31','d32','d33']
df = pd.DataFrame(
{'DIVISION': la,
'DEPT_CODE': lb,
'DEPT_NAME': lc
})
lW = list(zip(df['DIVISION'].astype(str) , df['DEPT_CODE'].astype(str)))
lpt = list(zip(df['DEPT_CODE'].astype(str) , df['DEPT_NAME'].astype(str)))
curHead = ""
r=0
c=-1
for head, DPT in lW:
if not curHead==head:
curHead = head
c+=1
r=0
dq=df.query('DIVISION=='+head)
lpt = list(zip(dq['DEPT_CODE'].astype(str) , dq['DEPT_NAME'].astype(str)))
t=ttk.Labelframe(self,text=head)
t.grid(column=c, row=0, sticky='nw')
self.checkGrpDept= clsGrpCheckButton(t,lpt,lambda:self.checkGrpDept.chkGrpGetValue(),True)
self.checkGrpDept.chkGrpGetChkObj(DPT).grid(column=c, row=r, sticky='nw')
t.rowconfigure(r, weight=1)
t.columnconfigure(c, weight=1)
r+=1
def wQuit(self):
self.destroy()
app = clsWindows()
app.mainloop()
Example of issue
finally, I came up with a solution by using partial when assigning the command to each checkbutton.
Here is the full code if someone faces a similar issue
import tkinter as tk
from tkinter import ttk
import pandas as pd
from functools import partial
class clsGrpCheckButton():
def __init__(self,pContainer, pLstVal,pCommand,pInitValue=True):
self.grpChk=[0]*len(pLstVal)
self.grpKey=[0]*len(pLstVal)
self.grpLstVal = [0]*len(pLstVal)
self.grpVariable= [0]*len(pLstVal)
self.grpActiveKeys = [0]
for l,t in enumerate(pLstVal):
#l : index of the list of tuples
self.grpKey[l] = t[0]
self.grpLstVal[l] = t[1]
self.grpVariable[l] = tk.StringVar()
self.grpChk[l] = ttk.Checkbutton(pContainer, text=self.grpLstVal[l],
state='active' ,
onvalue= self.grpKey[l],
offvalue= '',
variable = self.grpVariable[l],
command=partial(pCommand,self))
#get default value
if pInitValue :
self.grpVariable[l].set(self.grpKey[l])
self.grpActiveKeys.append(self.grpKey[l])
#get the index in the list of checkboxes
# depending on the key
def chkGrpGetIdx(self, pKey):
i=0
while i <len(self.grpKey):
if self.grpKey[i]==pKey :
return i
i=len(self.grpKey)
else:
i+=1
def chkGrpSetValue(self, pKey, pValue):
#need find correct index first
i=self.chkGrpGetIdx(pKey)
self.grpVariable[i] = pValue
#return the list of keys of the group
def chkGrpKeyLst(self):
return self.grpKey
#return the checkox element of the group of checkox
def chkGrpGetChkObj(self,pKey):
i=self.chkGrpGetIdx(pKey)
return self.grpChk[i]
#action when check/uncheck
#at list one element should be active
def chkGrpGetValue(self):
i=0
r=len(self.grpVariable)
self.grpActiveKeys.clear()
while i < len(self.grpVariable):
if self.grpVariable[i].get() =='':
r-=1
else:
self.grpActiveKeys.append(self.grpKey[i])
i+=1
if r==0:
self.grpVariable[0].set(self.grpKey[0])
self.grpActiveKeys.append(self.grpKey[0])
print(self.grpActiveKeys)
#to avoid accessing to class attribute directly
def chkGetCheckedValues(self):
return self.grpActiveKeys
class clsWindows(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
la = [1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3]
lb= [10,11,12,14,15,20,21,22,23,24,25,26,30,31,32,33]
lc=['d10','d11','d12','d14','d15','d20','d21','d22','d23','d24','d25','d26','d30','d31','d32','d33']
df = pd.DataFrame(
{'DIVISION': la,
'DEPT_CODE': lb,
'DEPT_NAME': lc
})
lW = list(zip(df['DIVISION'].astype(str) , df['DEPT_CODE'].astype(str)))
lpt = list(zip(df['DEPT_CODE'].astype(str) , df['DEPT_NAME'].astype(str)))
curHead = ""
r=0
c=-1
for head, DPT in lW:
if not curHead==head:
curHead = head
c+=1
r=0
dq=df.query('DIVISION=='+head)
lpt = list(zip(dq['DEPT_CODE'].astype(str) , dq['DEPT_NAME'].astype(str)))
t=ttk.Labelframe(self,text=head)
t.grid(column=c, row=0, sticky='nw')
checkGrpDept= clsGrpCheckButton(t,lpt,clsGrpCheckButton.chkGrpGetValue,True)
checkGrpDept.chkGrpGetChkObj(DPT).grid(column=c, row=r, sticky='nw')
t.rowconfigure(r, weight=1)
t.columnconfigure(c, weight=1)
r+=1
def wQuit(self):
self.destroy()
app = clsWindows()
app.mainloop()
I'm making a small GUI application that deals with grades and whatnot and outputs the highest grades and etc.
Here's a part of the code:
root = Tk()
gradeList = []
def addGradeObject(name, percentage):
gradeList.append(Grade(name, percentage))
updateOutput()
print(gradeList)
def undoAdd():
try:
gradeList.pop(len(gradeList) - 1)
updateOutput()
print(gradeList)
except Exception:
pass
def updateOutput():
highestGrade.setText(highest_grade(gradeList))
lowestGrade.setText(lowest_grade(gradeList))
numFailed.setText(num_failed(gradeList))
addButton = Button(text = "Add Grade", command = lambda: addGradeObject (entryA.get(), entryB.get())).grid(row = 1, column = 4)
undoButton = Button(text = "Undo", command = undoAdd).grid(row = 2, column = 4)
entryA = Entry()
entryA.grid(row = 1, column = 1)
entryB = Entry()
entryB.grid(row = 1, column = 2)
highestGrade = Entry()
highestGrade.grid(row = 2, column = 1)
lowestGrade = Entry()
lowestGrade.grid(row = 3, column = 1)
numFailed = Entry()
numFailed.grid(row = 4, column = 1)
root.title("Grade Checker")
root.mainloop()
The problem is I'm getting this error:
AttributeError: 'Entry' object has no attribute 'setText'
I don't understand. When you create an Entry box, doesn't the object of class "Entry" have an attribute/method that allows you to set text to it? I really don't know why it's not working
Entry methods can be found here. As far as I can tell, there is no setText method. There is however an insert method that you could use to set the text (though you might wand to delete the current text first).
def set_text(entry, text):
entry.delete(0, tkinter.END)
entry.insert(0 text)
Alternatively, you can hook the entry up to a StringVar and use it's .set() and .get() methods. e.g. (from the linked docs above):
v = StringVar()
e = Entry(master, textvariable=v)
e.pack()
v.set("a default value")
s = v.get()
I am new to python and recently i make dictionary using Python and Sqlite3 with tkinter.
When I run the code it returns multiple line in IDLE but in the GUI its only display the last result. I would like to display all the information in the GUI. Thanks you for any help and suggestion.
import tkinter
import sqlite3
class Dictionary:
def __init__(self,master =None):
self.main_window = tkinter.Tk()
self.main_window.title("Lai Mirang Dictionary")
self.main_window.minsize( 600,400)
self.main_window.configure(background = 'paleturquoise')
self.frame1 = tkinter.Frame()
self.frame2= tkinter.Frame(bg = 'red')
self.frame3 =tkinter.Text( foreground = 'green')
self.frame4 = tkinter.Text()
self.lai_label = tkinter.Label(self.frame1,anchor ='nw',font = 'Times:12', text = 'Lai',width =25,borderwidth ='3',fg='blue')
self.mirang_label = tkinter.Label(self.frame1,anchor ='ne',font = 'Times:12', text = 'Mirang',borderwidth ='3',fg ='blue')
self.lai_label.pack(side='left')
self.mirang_label.pack(side = 'left')
self.kawl_buttom= tkinter.Button(self.frame2 ,fg = 'red',font = 'Times:12',text ='Kawl',command=self.dic,borderwidth ='3',)
self.lai_entry = tkinter.Entry(self.frame2, width= 28, borderwidth ='3',)
self.lai_entry.focus_force()
self.lai_entry.bind('<Return>', self.dic,)
self.lai_entry.configure(background = 'khaki')
self.lai_entry.pack(side='left')
self.kawl_buttom.pack(side = 'left')
self.value = tkinter.StringVar()
self.mirang_label= tkinter.Label(self.frame3,font = 'Times:12',fg = 'blue',textvariable = self.value,justify = 'left',wraplength ='260',width = 30, height = 15,anchor = 'nw')
self.mirang_label.pack()
self.mirang_label.configure(background = 'seashell')
self.copyright_label = tkinter.Label(self.frame4,anchor ='nw',font = 'Times:12:bold',text = "copyright # cchristoe#gmail.com",width = 30,borderwidth = 3, fg = 'purple',)
self.copyright_label.pack()
self.frame1.pack()
self.frame2.pack()
self.frame3.pack()
self.frame4.pack()
tkinter.mainloop()
self.main_window.quit()
def dic(self, event = None):
conn = sqlite3.connect('C:/users/christoe/documents/sqlite/laimirangdictionary.sqlite')
c = conn.cursor()
kawl = self.lai_entry.get()
kawl = kawl + '%'
c.execute("SELECT * FROM laimirang WHERE lai LIKE ?", (kawl,))
c.fetchall
for member in c:
out = (member)
self.value.set(out)
print(out, )
dic=Dictionary()
You need to change:
c.execute("SELECT * FROM laimirang WHERE lai LIKE ?", (kawl,))
c.fetchall
for member in c:
out = (member)
self.value.set(out)
print(out, )
To:
for member in c.execute("SELECT * FROM laimirang WHERE lai LIKE ?", (kawl,)):
current_text = self.value.get()
new_text = current_text +( "\n" if current_text else "") +" ".join(member)
self.value.set(new_text)
The new version gets the current value of the StringVar value and then appends the results returned with a space inbetween for each row returned with each row on its own line. What you were doing however involved just updating the StringVar to be the current member object, and therefore it only ever had the last values.
This is how it looks with more than one line: