Search in list of tkinter widgets - python

I have some tkinter widgets stored in a list. I'd like to search for an object in that list, by I don't know the syntax.
import tkinter as tk
main = tk.Tk()
base = tk.Frame(main).pack()
l = []
for i in range(3):
et = tk.Label(base, text='label '+str(i))
et.pack()
l.append(et)
print(base.Label.!label in l)
main.mainloop()
Note.
Certainly this is a minimal example to understand where my mistake is.
The gui actually consists of an n x m matrix of tkinker entries, whose cells, rows, and columns should be dynamically added, deleted, modified, and even inserted or switched.
To do this, I have a dictionary that associates index tuples (i,j) with tkinter entries. When an entry is chosen with the mouse, that object is known, but what I really need to know is its index (i,j) to manage all the rest of the information (maths operations over arrays, etc).

To solve your problem you could use the index of separate list and use widget.grid_info() to retrieve the indices.
top_bar = []
sidebar = []
The rendering and the content have separated masters to achieve that the indexes of the lists, which starts with 0, match with the column and row of your inner grid.
The outer master could be populated like this, important to note is that Label on grid position 0,0 is not append to any of the lists but is gridded to fill the space. The grid_configure is just a optical improvement.
def outer_matrix():
for x in range(COLUMNS+1):
for y in range(ROWS+1):
ref = tk.Label(root,)#justify='center')
txt = None
if x == 0 and y != 0:
txt = y
top_bar.append(ref)
elif y == 0 and x != 0:
txt = x
sidebar.append(ref)
if txt != None or x == 0 and y == 0:
ref.config(text = txt, width=10)
ref.grid(row = y, column = x, sticky='nswe')
root.grid_columnconfigure(x,weight=1)
root.grid_rowconfigure(y,weight=1)
Full Example:
import tkinter as tk
ROWS = 6
COLUMNS = 6
top_bar = []
sidebar = []
def outer_matrix():
for x in range(COLUMNS+1):
for y in range(ROWS+1):
ref = tk.Label(root,)#justify='center')
txt = None
if x == 0 and y != 0:
txt = y
top_bar.append(ref)
elif y == 0 and x != 0:
txt = x
sidebar.append(ref)
if txt != None or x == 0 and y == 0:
ref.config(text = txt, width=10)
ref.grid(row = y, column = x, sticky='nswe')
root.grid_columnconfigure(x,weight=1)
root.grid_rowconfigure(y,weight=1)
else:
ref.destroy()
def inner_matrix():
for x in range(COLUMNS):
for y in range(ROWS):
ref = tk.Entry(inner_frame, width=10)
ref.grid(column=x,row=y, sticky='nswe')
inner_frame.grid_columnconfigure(x,weight=1)
inner_frame.grid_rowconfigure(y,weight=1)
ref.bind('<FocusIn>', lambda e:highlight(e))
ref.bind('<FocusOut>', lambda e:highlight(e))
def highlight(event):
info = event.widget.grid_info()
x_info = info['row']
y_info = info['column']
if 'FocusIn' in str(event):
top_bar[x_info].configure(bg='yellow')
sidebar[y_info].configure(bg='yellow')
if 'FocusOut' in str(event):
top_bar[x_info].configure(bg='SystemButtonFace')
sidebar[y_info].configure(bg='SystemButtonFace')
root = tk.Tk()
root.update()
inner_frame = tk.Frame(root,bg='red')
inner_frame.grid(row=1,column=1,
columnspan=COLUMNS,
rowspan = ROWS,
sticky = 'nswe')
outer_matrix()
inner_matrix()
root.mainloop()

Related

Add 2 numbers from separate buttons (tkinter)

I have 3 buttons I want to make it so when you press a button it sets the variable to a value.
press 1st button sets x to 1 2nd set y to 2 press 3rd add x and y together should return with 3 but it returns with 0.
I tried to make a 4th value where when it is set to 1 it would add both together. me and a friend tried making x and y at the beginning and integer int(x)= x
here is the code
import tkinter
window_main = tkinter.Tk(className='Tkinter - TutorialKart', )
window_main.geometry("400x200")
i=0
x=0
y=0
o = 0
p = 0
x = int(x)
y = int(y)
o = int(o)
p = int(p)
i=int(i)
def submitFunction() :
x = 1
print(x)
if o == 1:
x = 1
def a2():
y = 2
print(y)
if p == 1:
y = 2
def equ():
i = int(x + y)
return(i)
print(i)
button_submit = tkinter.Button(window_main, text ="set x to 1", command=submitFunction).grid(column = 1, row = 1)
button = tkinter.Button(window_main, text ="set y to 2", command=a2).grid(column = 2, row = 1)
button = tkinter.Button(window_main, text = "solve", command = equ). grid(column = 3, row = 1)
window_main.mainloop()
Given your code if you look carefully there are two Variables by the name 'x' and 'y' and whenever you press the button it does execute the button however the variables in inside those function are treated as local variables although you have defined them outside the function which should make them global variables.
But in the case of python global variables can be accessed inside the function but cannot be modified.
So in this case in order to treat the 'x' and 'y' with global scope so they can be modified or changed inside the function which reflects it variables outside, we use global Keyword inside function in order to make their scope global.
I have modified your code a bit.
from tkinter import *
import tkinter
window_main = Tk(className='Tkinter - TutorialKart', )
window_main.geometry("400x200")
i=0
x=0
y=0
o = 0
p = 0
def submitFunction() :
global x
x = 1
print(x)
if o == 1:
x = 1
def a2():
global y
y = 2
print(y)
if p == 1:
y = 2
def equ():
i = x + y
print(i)
return (i)
button_submit = tkinter.Button(window_main, text ="set x to 1", command=submitFunction).grid(column = 1, row = 1)
button = tkinter.Button(window_main, text ="set y to 2", command=a2).grid(column = 2, row = 1)
button = tkinter.Button(window_main, text = "solve", command = equ). grid(column = 3, row = 1)
window_main.mainloop()
If you want to read more about calling functions in tkinter without parenthesis click this link the person has explained it perfectly.
Also check variable scoping in Python link
You might get more clarity on understanding your problem after reading.

Issues with 2d array indexing python when making game

I'm attempting to program my own connect four game using Python. I'm trying to sort the circles that I have drawn into a 2d array. However when I try to assign my shape object to the array it gives me an index error. I can't really see an issue with counterrow and countrercolumn, can anyone else? Btw my space class just has an initialiser setting x1, x2, y1, y2, taken, and id
from tkinter import *
from space import *
master = Tk();
w = Canvas(master, width = 600, height = 500)
w.pack()
spaceList = []
for i in range(7):
spaceList.append([0] * 6)
currentmove = 'PLAYER1'
won = False
counterrow = 0
countercolumn = 0
for i in range(0,560,80):
for j in range(0,480,80):
w.create_oval(10+i, 10+j, 90+i, 90+j)
newspace = Space(10+i, 10+j, 90+i, 90+j, False, 'EMPTY')
spaceList[counterrow][countercolumn] = newspace
countercolumn = countercolumn + 1
counterrow = counterrow + 1
while(not won):
movecol = int(input("Please select a column!"))
def move(column):
for i in spaceList:
return 0
mainloop()
You have to reset the countercolumn:
for i in range(0,560,80):
# add this:
countercolumn = 0
for j in range(0,480,80):
# omitted
Otherwise it becomes seven and larger and you get an overflow.

Trouble with global dictionary

import tkinter as tk
#messagebox is not imported automatically w/ tkinter
from tkinter import messagebox as tkMessageBox
from tkinter import ttk
from random import random as rand
class Square(object):
""" class to use for each square """
def __init__(self):
self.mine_yn = False
self.flag_yn = False
self.prox_num = 0 # number of nearby mines, parse_mines() will fill this in.
self.button = None # ttk.Button instance.
def parse_mines():
"""Look at how many mines are next to a given square,
store in each Square instance that is inside of sqr_dict. """
global sqr_dict
global mine_frame
print('in parse_mines, sqr_dict='+str(sqr_dict))
def try_a_square(sq): #sq = coordinate string(key)
try:
if sqr_dict[sq].mine_yn == True: return 1
if sqr_dict[sq].mine_yn == False: return 0
except KeyError:
print('KeyError for '+sq)
return 0
n = 0
for x in range(5):
for y in range(4):
#check the 8 adjacent squares.
n = n + try_a_square('x'+str(x+1)+'y'+str(y+1))
n = n + try_a_square('x'+str(x+1)+'y'+str(y ))
n = n + try_a_square('x'+str(x+1)+'y'+str(y-1))
n = n + try_a_square('x'+str(x )+'y'+str(y+1))
n = n + try_a_square('x'+str(x )+'y'+str(y-1))
n = n + try_a_square('x'+str(x-1)+'y'+str(y+1))
n = n + try_a_square('x'+str(x-1)+'y'+str(y ))
n = n + try_a_square('x'+str(x-1)+'y'+str(y-1))
if sqr_dict[('x'+str(x)+'y'+str(y))].mine_yn == False:
(sqr_dict[('x'+str(x)+'y'+str(y))]).prox_num = n
print('x'+str(x)+'y'+str(y)+': '+str(n)) #(debug) print n for each sq
#sqr_dict[('x'+str(x)+'y'+str(y))].button.text=(str(n)) #(debug) show n on each button.
n = 0
def create_mine_field():
global mine_frame
global sqr_dict
sqr_dict = {}
mine_frame = tk.Toplevel(root)
mine_frame.grid()
#what to do if user hit 'X' to close window.
mine_frame.protocol("WM_DELETE_WINDOW", mine_frame_close)
# create grid of squares (buttons)
for x in range(5):
for y in range(4):
coord = 'x'+str(x) + 'y'+str(y)
sqr_dict[coord] = Square()
#print('coord='+coord) #debug
#populate with mines
if ( rand()*100 < mines_pct ):
sqr_dict[coord].mine_yn = True
print(str(sqr_dict[coord].mine_yn))
else:
sqr_dict[coord].mine_yn = False
if sqr_dict[coord].mine_yn:
t = '*'
else: t = ' '
# draw boxes
sqr_dict[coord].button = ttk.Button(mine_frame, text=t, width=3 )
sqr_dict[coord].button.grid(column=x, row=y)
# done, next: parse!
print('in create_mines, sqr_dict='+str(sqr_dict))
#parse_mines()
def root_close():
root.destroy()
def mine_frame_close():
root.destroy()
root = tk.Tk()
root.title("MineSweeper")
mines_pct = 20
start_button = ttk.Button(root,text="Start",command=create_mine_field)
start_button.pack()
root.mainloop()
I'm trying to make a simple minesweeper game with tkinter. If I run the above code a simple mine field appears. However if I uncomment the call to parser() then nothing shows up and it seems like it never finds any mines in the sqr_dict dictionary. (parser() will fill in the numbers of adjacent mines for each square)
I don't understand why this function would cause trouble before it is even called. No mine field appears when it's called. Please kindly give me your suggestions. Thanks!
The reason nothing shows up is because you are using both pack and grid on widgets that are children of the root window. Within any given window you must only use one or the other.

Python tkinter get() has no attribute

I'd like to use each variable in the list in an Entry widget, and get the text after pressing a button.
self.input_text = ['l1', 'l2', 'l3']
self.activeRow = 3
for e in self.input_text:
e = StringVar()
t = Entry(textvariable=e)
t.grid(row=self.activeRow,column=4,columnspan=2,sticky=W)
self.activeRow += 1
Eventually, I'd like to write the value of each variable in the list to a file but for now, I can't seem to print self.input_text[0] after the button press.
button1 = Button(text='Write',command=self.__writeNewInfo, width = 15)
button1.grid(row=self.activeRow,column=5,sticky=W)
def __writeNewInfo(self):
x = self.input_text[0].get()
y = self.input_text[1].get()
z = self.input_text[2].get()
print x
print y
print z
You are doing the wrong thing when creating the instances of StringVar. You set e to be the item in the list, then overwrite it as an empty StringVar. Instead try:
for i, e in enumerate(self.input_text):
self.input_text[i] = StringVar() #Overwrite this item in the list
self.input_text[i].set(e) #Set the StringVar to take the value from the list
t = Entry(textvariable=self.input_text[i])
t.grid(row=self.activeRow,column=4,columnspan=2,sticky=W)
self.activeRow += 1
self.input_text is basically a list of strings. If you want to retrieve something from an entry you need to keep the variable of this entry and call get() on it.
for example:
self.input_text = ['l1', 'l2', 'l3']
self.entries = []
self.activeRow = 3
for e in self.input_text:
e = StringVar()
t = Entry(textvariable=e)
t.grid(row=self.activeRow,column=4,columnspan=2,sticky=W)
self.activeRow += 1
self.entries.append(t)
button1 = Button(text='Write',command=self.__writeNewInfo, width = 15)
button1.grid(row=self.activeRow,column=5,sticky=W)
def __writeNewInfo(self):
x = self.entries[0].get()
y = self.entries[1].get()
z = self.entries[2].get()
print x
print y
print z

Tkinter label height to fit content

I have a label into which I am going to put content of different sizes. I would like to know how high I need to make the label so that I can size the window so it can stay the same for the different content sizes. I have a strategy, but it seems more complicated then it should be.
I want to set a label to a given width and wraplength:
l = Label(root)
l['width'] = 30
l['wraplength'] = 244
l['text'] = "testing this"
Now I want to query the label to find how many lines are used. l['height'] stays at 0, so the best I have been able to come up with is to use l.winfo_height() and convert the height given in pixels to the number of lines used. Nothing in dir(l) seems to give me the information directly, but this strategy is fragile to font changes and other changes.
Any suggestions?
Update: using Brian Oakley's suggestion (which is similar to what I got on usenet) I have the following approximation to a solution (needs polishing, e.g. doesn't take into account that Label breaks at whitespace):
import Tkinter as Tk
import tkFont
import random
import sys
def genstr (j):
rno = random.randint(4,50)
ret_val = str(j) + ":"
for i in range (0, rno):
ret_val += "hello" + str(i)
return ret_val
def gendata (lh):
ret_val = []
for i in range(0,lh):
ret_val.append (genstr (i))
return ret_val
data = gendata (100)
root = Tk.Tk()
font = tkFont.Font(family='times', size=13)
class lines:
def __init__ (self):
self.lastct = 1 # remember where the cutoff was last work from there
def count (self, text, cutoff = 400):
global font
no_lines = 1
start_idx = 0
idx = self.lastct
while True:
if idx > len (text):
idx = len (text)
# shrink from guessed value
while font.measure (text[start_idx:idx - 1]) > cutoff:
if idx <= start_idx:
print "error"
sys.exit ()
else:
idx -= 1
self.lastct = idx - start_idx # adjust since was too big
# increase from guessed value (note: if first shrunk then done)
while (idx < len (text)
and font.measure (text[start_idx:idx]) < cutoff):
idx += 1
self.lastct = idx - start_idx # adjust since was too small
# next line has been determined
print "*" + text[start_idx:idx-1] + "*"
if idx == len(text) and font.measure (text[start_idx:]) < cutoff:
return no_lines
elif idx == len(text):
return no_lines + 1
else:
no_lines += 1
start_idx = idx - 1
idx = start_idx + self.lastct
lin = lines()
for i in range(0,len(data)):
lin.count(data[i], 450)
for i in range(0,min(len(data),10)):
l = Tk.Label(root)
l.pack()
l['text'] = data[i]
print i
no = lin.count (data[i], 450)
print "computed lines", no
l['width'] = 50
l['justify'] = Tk.LEFT
l['anchor'] = 'w'
l['wraplength'] = 450
l['padx']=10
l['pady'] = 5
l['height'] = no
l['font'] = font
if i % 2 == 0:
l['background'] = 'grey80'
else:
l['background'] = 'grey70'
root.mainloop()
You are correct that the height attribute doesn't change. That attribute doesn't tell you the actual height, only the height it is configured to be. The actual height depends on factors such as how much text is in it, the wrap length, the font, and how the widget geometry is managed.
tkinter Font objects have a measure method which lets you determine how tall and wide a string is for a given font. You can get the font for the widget and use that method to determine how much space is required for your string.

Categories