How can I make a re-grading button from tkinter entry? - python

I was making a vocab test program and I was making a grading button. But I faced a problem that when I press the grading twice, grade, entries, right, wrong keeps appends and I can't find the way to reset these lists. So, when I press the grade button again after I correct the answer, the grading overlaps and makes an error.
import tkinter
from tkinter import *
window = tkinter.Tk()
container = tkinter.Frame(window)
canvas = tkinter.Canvas(container)
window.title('Rescue word test')
window.geometry('640x480')
window.resizable(True, True)
scrollbar = tkinter.Scrollbar(container, orient="vertical", command=canvas.yview)
#scroll
main_frame = Frame(window)
main_frame.pack(fill=BOTH, expand=1)
my_canvas = Canvas(main_frame)
my_canvas.pack(side=LEFT, fill=BOTH, expand=1)
my_scrollbar = tkinter.Scrollbar(main_frame, orient=VERTICAL, command=my_canvas.yview)
my_scrollbar.pack(side=RIGHT, fill=Y)
my_canvas.configure(yscrollcommand=my_scrollbar.set)
my_scrollbar.bind('<Configure>', lambda e: my_canvas.configure(scrollregion= my_canvas.bbox("all")))
second_frame = Frame(my_canvas)
my_canvas.create_window((0,0), window= second_frame, anchor="nw")
def mouse_scroll(event):
my_canvas.yview_scroll(-1 * int((event.delta / 120)), "units")
my_canvas.bind_all("<MouseWheel>", mouse_scroll)
def getEntry( i ):
return list( second_frame.children.values() )[ i ]
Day21_eng = ['exquisite', 'acquisition', 'regulate', 'transportation', 'insight', 'straightforward', 'cultivate', 'innovation', 'preserve', 'odor', 'exception', 'munch', 'proclaim', 'slap', 'variability', 'investigate', 'flare', 'outpace', 'genuine', 'plead', 'fossilize', 'toil', 'drastic', 'withhold', 'inanimate', 'clockwise', 'amnesia', 'revive', 'theorize', 'culprit', 'limp', 'worn-out', 'indignity', 'span', 'bribe']
Day21_kor = [['우아한', '정교한', '절묘한'], ['취득', '획득', '습득'], ['규제하다', '통제하다'], ['운송', '운임', '추방'], ['통찰', '통찰력'], ['명확한', '솔직한'], ['경작하다', '기르다', '장려하다', '육성하다'], ['혁신'], ['보전', '보호지', '보호하다', '보존하다'], ['냄새', '악취', '기미', '낌새'], ['예외'], ['우적우적 먹다'], ['선언하다'], ['찰싹 때리다'], ['변화성', '가변성', '변용성'], ['조사하다'], ['불끈 성나게 하다', '이글거리다', '불꽃', '타오름'], ['앞지르다', '속도가 더 빠르다'], ['진짜의', '진품의'], ['탄원하다', '변호하다', '애원하다'], ['고착화하다', '화석화하다'], ['수고', '노고', '힘들게 일하다'], ['급격한', '극단적인'], ['보류하다', '유보하다'], ['생명 없는', '무생물의'], ['시계방향으로'], ['기억상실'], ['부활시키다', '되살아나게 하다'], ['이론화하다'], ['죄인', '범죄자', '장본인'], ['절뚝거리다', '느릿느릿 가다', '기운이 없는','축 처진'], ['닳아빠진', '진부한', '지친'], ['모욕', '무례', '치욕'], ['기간', '폭', '범위', '걸치다', '이르다'], ['뇌물을 주다', '뇌물']]
entries = []
grade =[]
def check():
for i, e in enumerate(entries):
value = e.get()
grade.append(str(value in Day21_kor[i]))
right = [i for i, value in enumerate(grade) if value == 'True']
wrong = [k for k, value in enumerate(grade) if value == 'False']
print(right) # I can check that these lists keep appends
print(wrong)
print(entries)
print(grade)
r = 0
for i in range(0, len(right)):
label_right = Label(second_frame, text='O', fg='Blue')
label_right.grid(column=3, row= right[r])
r += 1
w = 0
for i in range(0, len(wrong)):
label_wrong = Label(second_frame, text='X', fg= 'red')
label_wrong.grid(column=3, row= wrong[w])
w += 1
b = 0
for row, item in enumerate(Day21_eng):
global label_word
label_word = tkinter.Label(second_frame, text= item)
label_word.grid(column=0, row=row)
b += 1
#입력 값 35개
entry = tkinter.Entry(second_frame, width=30)
entry.grid(row=row, column=1, sticky='nsew')
# important to bind each one for access
entry.bind('<Return>', getEntry)
entries.append(entry) # save 'entry' into list
b_check = tkinter.Button(second_frame, text='grade', command=check)
b_check.grid(columnspan=2, row=36)
window.mainloop()
How can I reset these lists so I can re-grade the answers? I've tried adding right=[] wrong=[] entries=[] grade=[] and also used another list to get rid of the overlapped values in the list,
def check():
for i in range(0,35):
test_try_list[test_try].append(entry) # save 'entry' into list
for i, e in enumerate(test_try_list[test_try]):
value = e.get()
i_list.append(i)
print(i_list)
if len(i_list) > 35:
for i in range(0, len(i_list)):
new_i_list.append(i_list[i] % 35)
for v in new_i_list:
if v not in new_i_list2:
new_i_list2.append(v)
print(new_i_list2)
but it still doesn't work. How can I make button to re-grade?

You can use grade.clear() to clear list
def check():
grade.clear()
or you have to use global to inform function that it has to assign new list to external/global variable
def check():
global grade
grade = []
But I would run it without all these lists
def check():
correct = 0
wrong = 0
for i, (e, expected) in enumerate(zip(entries, Day21_kor)):
value = e.get()
if value in expected:
correct += 1
label = Label(second_frame, text='O', fg='Blue')
else:
wrong += 1
label = Label(second_frame, text='X', fg= 'red')
label.grid(column=3, row=i)
percent = correct/(correct+wrong)
print('Correct:', correct, '| Wrong:', wrong, '| Percent: {:.1%}'.format(percent))
Result:
Correct: 0 | Wrong: 35 | Percent: 0.0%
Correct: 1 | Wrong: 34 | Percent: 2.9%

Related

Run functions from list

trying to get the selected values from a list of checkbutton without having to create 10 or more checbutton and var.
i got this to test the idea
from tkinter import Tk, StringVar, Checkbutton, Button, BooleanVar
root = Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (w, h))
def def1():
print("1")
def def2():
print("2")
def def3():
print("3")
def def4():
print("4")
def def5():
print("5")
def def6():
print("6")
def letssee():
print(addlist)
nomtestes = ["def1", "def2", "def3", "def4", "def5", "def6"]
clltes = 0
rwwtes = 0
addlist=[]
username_cbs = dict()
for name in nomtestes:
if clltes == 5:
rwwtes += 1
clltes = 0
username_cbs[name] = Checkbutton(root, text=name, onvalue=True, offvalue=False)
username_cbs[name].var = BooleanVar()
username_cbs[name]['variable'] = username_cbs[name].var
username_cbs[name]['command'] = lambda w=username_cbs[name]: upon_select(w)
username_cbs[name].grid(row=rwwtes, column=clltes, pady=2)
clltes += 1
Button(root, text="OK",command=letssee).grid(column=0, row=5, padx=1, pady=15)
def upon_select(widget):
if widget.var.get() == True:
addlist.append(widget['text'])
else:
addlist.remove(widget['text'])
root.mainloop()
In this example im trying to print all the checkbuttons i selected, but, to run the funcions added to the addlist
Any ideia how to do this?
thanks
You can create a dictionary, mapping string values to respective functions, and then add or remove them to a list. You can add a function map and change the letssee() like this to check.
func_map = {"def1": def1, "def2": def2, "def3": def3, "def4": def4, "def5": def5, "def6": def6}
nomtestes = ["def1", "def2", "def3", "def4", "def5", "def6"]
def letssee():
print(addlist)
for i in addlist:
func_map[i]()
Out:['def2', 'def1']
2
1

Tkinter - python 3 - overlapping grid of checkboxes from a loop within a loop

I have this code here that runs a window that looks like this:
Once I click on one of those element buttons I get the isotope results shown here:
But if I select another button I just get an overwrite of the checkboxes shown here:
I cannot for the life of me find a way to destroy the previous checkboxes before showing the results. I have tried a number of ways and I am pretty sure that this here SO post is the answer but I am not 100% sure if I need to split my results into a range and append the button lists accordingly. I am trying but I am new to front-end progamming. Does anyone know how to get this thing working??
Here is the code:
import tkinter as tk
from tkinter import ttk
import os
import pandas as pd
__location__ = os.path.realpath(
os.path.join(os.getcwd(), os.path.dirname(__file__)))
class AppTBL(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.winfo_toplevel().title("Periodic Table of the Elements")
self.topLabel = tk.Label(
self, text="\n\n\n\nClick the element you would like information about.", font=20)
self.topLabel.grid(row=0, column=0, columnspan=18)
self.column1 = [
('H', 'Hydrogen', '\nAtomic # = 1\nAtomic Weight =1.01\nState = Gas\nCategory = Alkali Metals',),
('Li', 'Lithium', '\nAtomic # = 3\nAtomic Weight = 6.94\nState = Solid\nCategory = Alkali Metals'),
('Sod', 'Sodium', '\nAtomic # = 11\nAtomic Weight = 22.99\nState = Solid\nCategory = Alkali Metals'),
('K', 'Potassium', '\nAtomic # = 19\nAtomic Weight = 39.10\nState = Solid\nCategory = Alkali Metals'),
('Rb', 'Rubidium', '\nAtomic # = 37\nAtomic Weight = 85.47\nState = Solid\nCategory = Alkali Metals'),
('Cs', 'Cesium', '\nAtomic # = 55\nAtomic Weight = 132.91\nState = Solid\nCategory = Alkali Metals'),
('Fr', 'Francium', '\nAtomic # = 87\nAtomic Weight = 223.00\nState = Solid\nCategory = Alkali Metals')]
r = 1
c = 0
for b in self.column1:
self.elebtn = tk.Button(self,
text=b[0],
width=5,
height=2,
bg="grey",
command=lambda text=b: [self.name(text[1]), self.thebtn(text[1]), self.isotopes(text[0]), self.isodestroy]).grid(row=r, column=c)
r += 1
if r > len(self.column1):
r = 1
c += 1
self.infoLine = tk.Label(self, text="", justify='left')
self.infoLine.grid(row=1, column=3, columnspan=10, rowspan=4)
def name(self, text):
self.topLabel.config(text=text)
def info(self, text):
self.infoLine.config(text=text)
def thebtn(self, anything):
thefile = os.path.join(__location__, 'thefile.txt')
elementStr = []
elementStr.append(anything)
with open(thefile, 'w') as writeit:
for each in elementStr:
writeit.write(each)
def isotopes(self, text):
isofile = os.path.join(__location__, 'isotope.csv')
text = text
df = pd.read_csv(isofile, header=0)
subset = df[df['element'] == text.upper()]
subset = subset.astype({'isotope': int})
subindex = subset.index
numrows = len(subindex)
r = 1
c = 2
r1 = 1
c1 = 2
for index, row in subset.iterrows():
if numrows < 7:
text = row['isotope']
isobtn = tk.Checkbutton(self, text=text, width=3,
height=1, fg='brown').grid(row=r, column=c)
r += 1
if r > len(self.column1):
r = 1
c += 1
elif numrows >= 7:
text = row['isotope']
chkbtn = tk.Checkbutton(self, text=text, width=3,
height=1, fg='brown').grid(row=r1, column=c1)
r1 += 1
if r1 > len(self.column1):
r1 = 1
c1 += 1
def isodestroy(self):
pass
def endit(gui_sys):
# self.element_table_callback()
pass
root = tk.Tk()
root.eval('tk::PlaceWindow . center')
a = AppTBL(root)
a.grid(row=0, column=0, sticky='nsew')
a.bind('<Destroy>', endit)
a.bind('<Button-1>', AppTBL.isodestroy)
a.mainloop()
Thanks in advance! I have been stuck on this for two days now and I am just losing my mind here.
for isotopes.csv it will not work if you copy and paste code unless you add a csv file in the same directory.. here is a little bit for example if needed. You will need to turn it into a csv in notepad++ or notepad. I can also give the original file in a share drive if needed.
element
isotope
H
1
H
2
H
3
H
5
H
6
H
7
HE
10
HE
2
HE
3
HE
4
HE
5
HE
6
HE
7
HE
8
HE
9
The answer was:
for child in self.winfo_children():
if child.winfo_class() == 'Checkbutton':
child.destroy()
But that also included taking the results from a run, appending it to its own table in a database. then calling it again in another function that I had to run twice. once before the button was clicked and once after.
If you want to remove the previous checkboxes before displaying the result for the next element, then there is such an option.
A widget has a method to get a list of all of its children, widget.winfo_children().
You can create a container for checkboxes, such as a frame. And if necessary, remove all elements in the frame.
To see how this works, you can do the following:
in def __init__
def __init__(self, parent, *args, **kwargs):
...
# self.infoLine = tk.Label(self, text="", justify='left')
# self.infoLine.grid(row=1, column=3, columnspan=10, rowspan=4)
self.frame = tk.Frame(self)
self.frame.grid(row=1, column=3, columnspan=10, rowspan=4)
in def isotopes
def isotopes(self, text):
# destroy previous checkboxes
for child in self.frame.winfo_children():
child.destroy()
...
for index, row in subset.iterrows():
...
isobtn = tk.Checkbutton(self.frame, ...
...
chkbtn = tk.Checkbutton(self.frame, ...
You may need to redesign the interface to fit the frame into the window.

Why is my function not affecting a button in tkinter?

I'm trying to make Tic Tac Toe in python using tkinter, but I'm running into an issue. I'm trying to make it so when you click on one of the squares, it displays whichever player's symbol on that square. However, with my current function playerCheck, nothing happens when I press them. I can't figure out why this is happening, so I would appreciate any help.
Keep in mind I am in no way finished.
import tkinter as tk
Player1 = "X"
Player2 = "O"
turn = None
playerNumber = None
def playerCheck(function):
if turn == Player1:
playerNumber = Player1
function("white", playerNumber)
elif turn == Player2:
playerNumber = Player2
function("white", playerNumber)
def topLeft(color, player):
topL = tk.Button(text=player, fg="black", bg=color, height=5, width=10, command=lambda: playerCheck(topL)).grid(row=0,column=0)
def topMiddle(color, player):
topM = tk.Button(text=player, fg="black", bg=color, height=5, width=10, command=lambda: playerCheck(topMiddle)).grid(row=0,column=1)
def topRight(color, player):
topR = tk.Button(text=player, fg="black", bg=color, height=5, width=10, command=lambda: playerCheck(topRight)).grid(row=0,column=2)
def middleLeft(color, player):
midL = tk.Button(text=player, fg="black", bg=color, height=5, width=10, command=lambda: playerCheck(middleLeft)).grid(row=1,column=0)
def middleMiddle(color, player):
midM = tk.Button(text=player, fg="black", bg=color, height=5, width=10, command=lambda: playerCheck(middleMiddle)).grid(row=1,column=1)
def middleRight(color, player):
midR = tk.Button(text=player, fg="black", bg=color, height=5, width=10, command=lambda: playerCheck(middleRight)).grid(row=1,column=2)
def bottomLeft(color, player):
botL = tk.Button(text=player, fg="black", bg=color, height=5, width=10, command=lambda: playerCheck(bottomLeft)).grid(row=2,column=0)
def bottomMiddle(color, player):
botM = tk.Button(text=player, fg="black", bg=color, height=5, width=10, command=lambda: playerCheck(bottomMiddle)).grid(row=2,column=1)
def bottomRight(color, player):
botR = tk.Button(text=player, fg="black", bg=color, height=5, width=10, command=lambda: playerCheck(bottomRight)).grid(row=2,column=2)
def gameStart():
topLeft("white", "")
topMiddle("white", "")
topRight("white", "")
middleLeft("white", "")
middleMiddle("white", "")
middleRight("white", "")
bottomLeft("white", "")
bottomMiddle("white", "")
bottomRight("white", "")
turn = Player1
def Main():
a = tk.Tk()
a.title("Tick Tack Toe")
a.geometry("250x250")
gameStart()
a.mainloop()
Main()
The argument to playerCheck() is a button, not a function. You should change the button's text and color, not try to call it.
def playerCheck(button):
global turn
button['text'] = turn
button['fg'] = "white"
if turn == Player1:
turn = Player2
else:
turn = Player1
Here is an example of a completely working tic-tac-toe made with tkinter. I used your Button approach. If you are going to make anything with tkinter it should look a lot more like the below code. I commented it all over the place to give you a bit of a guide to what everything is doing, but I did not dumb down the code. Some parts may be very hard for you to understand (like the logic in win_or_block()). That's a good thing. It will force you to research and get better.
tips:
Learn how to create and use classes, so you can separate and organize your code into meaningful chunks.
Your approach is to manually define each individual thing, even if they are the exact same thing. You should consider a more dynamic approach where you create something once and let a loop create and position a bunch of copies/instances of it.
If your code was written properly, according to the design that you laid out, you would technically have a Button that recreates itself when it is clicked. This type of circular logic is bad.
Worry less about Buttons and more about game logic. Your graphics should simply express a condition. You're on your way to making the graphics serve as the logic behind your conditions, and that is a terrible approach.
#python 3.8+
from tkinter import Tk, Frame, Button, Entry, StringVar, IntVar, Checkbutton
from statistics import mean
from random import choice
class Splash(Frame):
WIDTH = 300
HEIGHT = 190
def __init__(self, master, callback, *args, **kwargs):
Frame.__init__(self, master, *args, **kwargs)
self.callback = callback
self.message = StringVar()
self.tricky = IntVar()
#just to make some lines shorter
e = dict(font='calibri 18 bold', bg=self['bg'], border=0, justify='center', textvariable=self.message)
b = dict(font='calibri 18 bold', bg='black', fg='white')
c = dict(font='calibri 16 bold', bg=self['bg'])
Entry(self, **e).place(width=280, height=40, x=10, y=5)
Checkbutton(self, text="tricky", variable=self.tricky, **c).place(width=200, height=30, x=50, y=50)
Button(self, text='1 Player' , command=lambda: self.remove(True) , **b).place(width=200, height=45, x=50, y=90)
Button(self, text='2 Players', command=lambda: self.remove(False), **b).place(width=200, height=45, x=50, y=140)
def remove(self, auto):
self.place_forget()
self.callback(auto)
class Board(Frame):
#if you change this you need to change the font size in self.default_cell and vise-versa
WIDTH = 450
HEIGHT = 393
def __init__(self, master, callback, *args, **kwargs):
Frame.__init__(self, master, *args, **kwargs)
#cell default properties
self.default_cell = dict(
foreground = 'black',
background = 'black',
activebackground = 'black',
disabledforeground = 'black',
text = "",
font = "consolas 48 bold",
relief = 'sunken',
state = 'normal',
width = 4,
height = 1,
)
#make board
for i in range(9):
cell = Button(self, **self.default_cell)
cell.config(command=lambda c=cell, i=i: callback(c, i))
cell.grid(row=int(i//3), column=i%3, sticky='nswe')
def reset(self):
for cell in self.winfo_children():
cell.config(**self.default_cell)
#used to stop cell clicks while splash screen is visible
def lock(self):
for cell in self.winfo_children():
cell.config(state='disabled')
class Game(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
#current player
self.player = 0
#True: against computer - False: against 2nd human
self.auto = False
#stores moves
self.moves = [0]*9
#player colors
self.color = ['red', 'green']
# [crossed swords, flag outline]
self.marks = [chr(9876), chr(9872)]
#positions
self.edge = {1, 7, 3, 5}
self.corner = {0, 6, 2, 8}
#True = hard | False = easy
self.tricky = True
'''
numbers that can't average together with 0 to equal each other
where [n, n, n] is any given winning combination in tic-tac-toe
consider the mean of: [1, 2, 0] vs [1, 1, 1]
against the mean of: [1, 4, 0] vs [1, 1, 1]
'''
self.pids = [1, 4]
#init board
self.board = Board(self, self.move)
self.board.grid(row=0, column=0, sticky='nswe')
#init "game over" screen and place() properties
self.splash = Splash(self, callback=self.reset, bg='gray92')
self.splashprops = dict(
x = (Board.WIDTH-Splash.WIDTH)/2,
y = (Board.HEIGHT-Splash.HEIGHT)/2,
width = Splash.WIDTH,
height = Splash.HEIGHT
)
self.show_splash("Connect 3 And Conquer")
'''
This method is tricky. It is used to check:
1: if the current player has won (id=current_id, n=4)
2: if the computer can win (id=player2_id, n=3)
3: if the computer can block a win (id=player1_id, n=3)
'''
def win_or_block(self, id, n=3):
best = -1
try:
for a in range(3):
#vertical
m = self.moves[a::3]
l = list(filter(lambda i: i>0, m))+[id]
if len(l) == n and mean(l) == id:
best = (m.index(0) * 3) + a
break
#horizontal
m = self.moves[a*3:a*3+3]
l = list(filter(lambda i: i>0, m))+[id]
if len(l) == n and mean(l) == id:
best = m.index(0) + (a * 3)
break
#back slash
if best < 0:
m = self.moves[0::4]
l = list(filter(lambda i: i>0, m))+[id]
if len(l) == n and mean(l) == id:
best = m.index(0) * 4
#forward slash
if best < 0:
m = self.moves[2::2][0:3]
l = list(filter(lambda i: i>0, m))+[id]
if len(l) == n and mean(l) == id:
best = (m.index(0) + 1) * 2
except ValueError:
#m.index(0) does not exist, current player has won
best = 0
return best
def best(self, index):
best = -1
#computer checks if it can win, and if not, then if it can block
for i in range(1, -1, -1):
best = self.win_or_block(self.pids[i])
if best > -1:
break
#if the computer cannot win or there is nothing to block
if best < 0:
avail = {i for i, v in enumerate(self.moves) if v == 0}
c = list(self.corner.intersection(avail)) #corners
e = list(self.edge.intersection(avail)) #edges
#choose the middle or mirror opposite of the last move
if self.tricky:
b = 4 if 4 in avail else abs(index - 8)
if b in avail:
best = b
#attempt a random choice in this order: corner, center, edge
if best < 0:
if len(c):
best = choice(c)
elif 4 in avail:
best = 4
else:
best = choice(e)
return best
def move(self, button, index):
#mark the square
button.config(bg=self.color[self.player], text=self.marks[self.player], state='disabled')
#record the move
self.moves[index] = self.pids[self.player]
#game over screens
if self.win_or_block(self.pids[self.player], 4) > -1: #check win
self.show_splash(f'Player {self.player+1} Has Conquered!')
return
elif self.moves.count(0) == 0: #check "no more moves"
self.show_splash('Stalemate!')
return
#derive next player ~ Player1 = 0, Player2 = 1
self.player = (self.player + 1)%2
if self.auto and self.player == 1:
#invoke the button with the child index that corresponds to the "best" move
self.board.winfo_children()[self.best(index)].invoke()
def show_splash(self, msg):
self.board.lock()
self.splash.message.set(msg)
self.splash.place(**self.splashprops)
def reset(self, auto):
self.tricky = bool(self.splash.tricky.get())
self.auto = auto
self.player = 0
self.moves = [0]*9
self.board.reset()
if __name__ == '__main__':
app = Game()
app.title("Connect 3 And Conquer")
#lock down the window size
app.minsize(Board.WIDTH, Board.HEIGHT)
app.maxsize(Board.WIDTH, Board.HEIGHT)
app.mainloop()
aside:
I understand you are new. Don't take my criticisms to heart. The point is to make you better. Anybody could come in here and tell you how to make poor code work. My agenda is to stop you from writing poor code in the first place - by giving you an excellent example and explaining where you went wrong.
Cheers

Tkinter Pressing Buttons to produce a list

I'm trying to make a system where buttons are generated from a list, then as each one is pressed that button disappears and updates a value in a list it has created of equal length in the order the buttons were pressed i.e. [1,3,2,4] would mean that the first button was pressed first, then the 3rd, 2nd etc.
I've currently have it at the stage where the buttons will disappear but I can't yet have it increment its order or produce a list.
def buttonSelected(k, buttonNames, order, itemButton, buttonOrder):
order = order + 1
itemButton[k].destroy()
buttonOrder[k] = order
orderText = ["first, second, third"]
if order <= 2:
lbl.configure(text="Which button do you want to click first?")
else:
lbl.configure(text="Which button do you want to click %sth?" % order)
return order
def chooseOrder(buttonNames, order, buttonOrder):
orderText = ["first, second, third"]
itemButton = []
for i in range(len(buttonNames)):
print i
itemButton.append('')
itemButton[i] = tkinter.Button(window, text=buttonNames[i], command=lambda c=i: buttonSelected(c, buttonNames, order, itemButton, buttonOrder), font=("Helvetica", 10))
itemButton[i].pack()
if order <= 2:
lbl.configure(text="Which button do you want to click first?")
else:
lbl.configure(text="Which button do you want to click %sth?" % order)
buttonOrder = []
for i in range(len(buttonNames)):
buttonOrder.append(0)
chooseOrder(buttonNames, 0, buttonOrder)
My order doesn't iterate because I haven't returned it and I don't know how to return a list either.
This is my complete code:
# import and rename the 'tkinter' module for < Python 3.3
import Tkinter as tkinter
import os
class Checkbar(tkinter.Frame):
def __init__(self, parent=None, picks=[], side=tkinter.TOP, anchor=tkinter.W):
tkinter.Frame.__init__(self, parent)
self.vars = []
for pick in picks:
var = tkinter.IntVar()
chk = tkinter.Checkbutton(self, text=pick, variable=var, font=("Helvetica", 10))
chk.pack(side=side, anchor=anchor, expand=tkinter.YES)
self.vars.append(var)
def state(self):
return map((lambda var: var.get()), self.vars)
def moduleSelected(k, wantedModules, order, itemButton, moduleOrder):
order = order + 1
#for j in range(len(itemButton)):
itemButton[k].destroy()
moduleOrder[k] = order
#chooseOrder(wantedModules, order)
orderText = ["first, second, third"]
if order <= 2:
lbl.configure(text="Which item do you want appearing first?")
else:
lbl.configure(text="Which item do you want appearing %sth?" % order)
if order == len(moduleOrder):
moduleOrder1Finished = 1
return order
def chooseOrder(wantedModules, order, moduleOrder):
orderText = ["first, second, third"]
itemButton = []
for i in range(len(wantedModules)):
print i
itemButton.append('')
itemButton[i] = tkinter.Button(window, text=wantedModules[i], command=lambda c=i: moduleSelected(c, wantedModules, order, itemButton, moduleOrder), font=("Helvetica", 10))
itemButton[i].pack()
print wantedModules
if order <= 2:
lbl.configure(text="Which button do you want appearing first?")
else:
lbl.configure(text="Which button do you want appearing %sth?" % order)
def chooseOrders(moduleList, moduleSelectColumn, order):
wantedModules = []
moduleOrder = []
for i in range(len(moduleList)):
if moduleList[i] == 1:
wantedModules.append(moduleSelectColumn[i])
moduleOrder.append(0)
chooseOrder(wantedModules, order, moduleOrder)
def modulesSelected(modules1, moduleSelectColumn1, modules2, moduleSelectColumn2, moduleSelectButton):
moduleList1 = modules1.state()
moduleList2 = modules2.state()
modules1.destroy()
modules2.destroy()
moduleSelectButton.destroy()
moduleOrder1 = chooseOrders(moduleList1, moduleSelectColumn1, 0)
moduleOrder2 = chooseOrders(moduleList2, moduleSelectColumn2, 0)
def produceSurvey():
moduleSelectColumn = []
for i in range(4):
moduleSelectColumn.append([])
moduleSelectColumn[1] = ['cheese', 'wine', 'bread', 'cereal']
moduleSelectButton = tkinter.Button(window, text="Finish Selecting", bg="#ffffff", font=("Helvetica", 16), command=lambda: modulesSelected(modules1, moduleSelectColumn[1], modules2,moduleSelectColumn[2], moduleSelectButton))
moduleSelectButton.pack(side=tkinter.TOP)
modules1 = Checkbar(window, moduleSelectColumn[1])
modules2 = Checkbar(window, moduleSelectColumn[2])
modules1.pack(side=tkinter.LEFT)
modules2.pack(side=tkinter.RIGHT)
lbl.configure(text="Select which buttons you want to Select?")
window.geometry("720x720")
def analSurvey():
print "analysing survey!"
def optionButton(option, option1, option2):
option1.destroy()
option2.destroy()
if option == 1:
produceSurvey()
elif option == 2:
analSurvey()
def callback_and_hide(button):
callback()
button.destroy()
def callback():
lbl.configure(text="What would you like to do?")
option1 = tkinter.Button(window, text="Help JameswDemps on stackexchange", bg="#ffffff", font=("Helvetica", 16), command=lambda: optionButton(1, option1, option2))
option1.pack()
option2 = tkinter.Button(window, text="go to a blank page", bg="#ffffff", font=("Helvetica", 16), command=lambda: optionButton(2, option1, option2))
option2.pack()
# create a new window
window = tkinter.Tk()
# set window title
window.title("Button Presser")
# Gets the requested values of the height and widht.
windowWidth = window.winfo_reqwidth()
windowHeight = window.winfo_reqheight()
# Gets both half the screen width/height and window width/height
positionRight = int(window.winfo_screenwidth()/2 - windowWidth/2)
positionDown = int(window.winfo_screenheight()/2 - windowHeight/2)
# Positions the window in the center of the page.
window.geometry("+{}+{}".format(positionRight-250, positionDown-150))
window.geometry("720x480")
# set the window background to hex code ...
window.configure(background="#ffffff")
# create a label widget call 'lbl'
lbl = tkinter.Label(window, text="Welcome to the Button presser!", bg="#ffffff", font=("Helvetica", 24))
# creat a text entry widget called 'ent'
#ent = tkinter.Entry(window)
# create a button widget called btn
startBtn = tkinter.Button(window, text="Start", command=lambda: callback_and_hide(startBtn), font=("Helvetica", 16))
# pack (add) the widget into the window
lbl.pack()
#ent.pack()
startBtn.pack()
# draw the window, and start the 'application'
window.mainloop()
RESULT = []
def moduleSelected(k, wantedModules, order, itemButton, moduleOrder):
order = order + 1
#for j in range(len(itemButton)):
itemButton[k].destroy()
moduleOrder[k] = order
print k
RESULT.append(k+1)
print RESULT # -> This will print the output you want.
#chooseOrder(wantedModules, order)
orderText = ["first, second, third"]
if order <= 2:
lbl.configure(text="Which item do you want appearing first?")
else:
lbl.configure(text="Which item do you want appearing %sth?" % order)
if order == len(moduleOrder):
moduleOrder1Finished = 1
return order
I have added a RESULT list to store the order of buttons in which they are clicked.

OptionMenu changes on second function pass Tkinter Python 2.7

The problem I am having is that the function below works fine on the first pass with regards to displaying the chosen value at the top of the OptionMenu before the 'Enter' button is clicked:
First pass
However on a second pass and any future passes to the function the chosen value is not displayed at the top of the OptionMenu (although if selected it will be used) before the 'Enter' Button is pressed:
Second etc. passes
The code is:
def load_create_list(self):
self.app = Toplevel(self.parent)
self.parent.withdraw()
self.app.title("Rgs2")
self.student = []
self.student1=[]
self.gs1=0
self.ng1=0
options = ["2", "3", "4", "5", "6", "7"]
self.results=[]
self.ngg=StringVar() #for the option menu widget the variable must be a stringVar not a normal string, int etc.
self.ng = Label(self.app, text="Number of groups")
self.ng.grid(row=2, column=0)
self.ngg.set("3")
self.ng1 = OptionMenu(self.app, self.ngg, *options)
self.ng1.grid(row=2, column=1, ipadx=10)
for i in range(0,len(self.x3)):
self.L1 = Label(self.app,text="Student")
self.L1.grid(row=i+4, column=0)
self.en = Entry(self.app)
self.en.insert(END, self.x3[i])
self.en.grid(row=i+4, column=1)
self.student.append(self.en)
self.el = int(len(self.student)+1)
for h in range(self.el,self.el+5):
self.L2 = Label(self.app,text="Student")
self.L2.grid(row=h+4, column=0)
self.em = Entry(self.app)
self.em.grid(row=h+4, column=1)
self.student.append(self.em)
button=Button(self.app,text="enter",command=lambda : self.hallo2()).grid(row=h+7,column=0)
button=Button(self.app,text="Save List",command=lambda : self.save_list2()).grid(row=h+7,column=1)
I have tried everything but can't understand what might be causing this issues. Any help would be gladly appreciated. Seems like very odd behaviour could it be something outside of the function that is causing the issue?
Full code is:
from Tkinter import *
from tkFileDialog import askopenfile
class main_menu:
def __init__(self, parent):
self.myParent = parent
self.myContainer1 = Frame(parent)
parent.title("Random Group Sorter")
self.myContainer1.pack()
self.button1 = Button(self.myContainer1, command = lambda : self.hope())
self.button1.configure(text="Create List", height = 1, width = 20, font=("Arial", 16))
self.button1.pack(side=TOP)
self.button1.focus_force()
self.button3 = Button(self.myContainer1, command = lambda : self.hope1())
self.button3.configure(text="Load List", height = 1, width = 20, font=("Arial", 16))
self.button3.pack(side=TOP)
self.button2 = Button(self.myContainer1, command = lambda : exit(), )
self.button2.configure(text="Exit",height = 1, width = 20, font=("Arial", 16))
self.button2.pack(side=TOP)
def hope(self):
self.myParent.destroy()
create_list()
def hope1(self):
self.myParent.destroy()
load_list()
class create_list():
def __init__(self):
parent = Tk()
self.parent=parent
self.student = []
self.student1 =[]
self.student2 =[]
self.fake_student = []
self.results=[]
self.ngg = StringVar()# for the option menu widget the variable must be a stringVar not a normal string, int etc.
self.ng = Label(text="Number of groups")
self.ng.grid(row=2, column=0)
self.ng = OptionMenu(parent, self.ngg, "2", "3", "4", "5","6")
self.ng.grid(row=2, column=1)
for i in range(3,21):
self.L1 = Label(text="Student")
self.L1.grid(sticky=E)
self.en = Entry(parent)
self.en.grid(row=i, column=1)
self.student.append(self.en)
button=Button(parent,text="Enter",command=lambda : self.hallo()).grid(row=23,column=0)
button=Button(parent,text="Save List",command=lambda : self.save_list()).grid(row=23,column=1)
parent.mainloop()
def hallo(self):
self.gs1 = int(len(self.student))
self.ng1 = int(self.ngg.get())# still need to use .get even though a stringvar
for entry in self.student:
self.student1.append(entry.get())
self.student1 = filter(None, self.student1)
for i in self.student1:# this is added as there are duplicate entries in the student list if saved
if i not in self.student2:
self.student2.append(i)
self.parent.destroy()
lis_an(self.student2,self.ng1)
def save_list(self):
for entry in self.student:
self.student1.append(entry.get())
self.student1 = filter(None, self.student1)
import tkFileDialog
root = Tk()
root.withdraw()
file_name = tkFileDialog.asksaveasfile(parent=root)
root.destroy()
print >>file_name, "\n"
print >>file_name, "\n".join(self.student1)
file_name.close()
class load_list:
def __init__(self):
self.x = []
self.x3 = []
self.load_clean()
self.root = Tk()
def load_clean(self):#option to load an already created file, cleans unwanted info from the list
import tkFileDialog
root = Tk()
root.withdraw()
file_name = tkFileDialog.askopenfile(parent=root)
root.destroy()
self.x = file_name.readlines()
file_name.close()
x2 =self.x[:]
for z in self.x:
if z [0:6]== "Random" or z == '\n' or z[0:5] == "Group":
x2.remove(z)
for c in range (0,len(x2)):
v = x2[c].rstrip()# this strip spaces and \n from each list item and returns the cleaned up string
self.x3.append(v)
self.load_create_list()
def load_create_list(self):
parent = Tk()
self.parent=parent
self.student = []
self.student1=[]
self.gs1=0
self.ng1=0
self.results=[]
self.ngg = StringVar()# for the option menu widget the variable must be a stringVar not a normal string, int etc.
self.ng = Label(text="Number of groups")
self.ng.grid(row=2, column=0)
self.ng = OptionMenu(parent, self.ngg, "2", "3", "4", "5", "6")
self.ng.grid(row=2, column=1)
for i in range(0,len(self.x3)):
self.L1 = Label(text="Student")
self.L1.grid(row=i+3, column=0)
self.en = Entry(parent)
self.en.insert(END, self.x3[i])
self.en.grid(row=i+3, column=1)
self.student.append(self.en)
self.el = int(len(self.student)+1)
for h in range(self.el,self.el+5):
self.L2 = Label(text="Student")
self.L2.grid(row=h+3, column=0)
self.em = Entry(parent)
self.em.grid(row=h+3, column=1)
self.student.append(self.em)
button=Button(parent,text="enter",command=lambda : self.hallo2()).grid(row=h+6,column=0)
button=Button(parent,text="Save List",command=lambda : self.save_list2()).grid(row=h+6,column=1)
parent.mainloop()
def hallo2(self):
self.student2= []
self.gs1 = int(len(self.student))
self.ng1 = int(self.ngg.get())# still need to use .get even though a stringvar
for entry in self.student:
self.student1.append(entry.get())
self.student1 = filter(None, self.student1)
for i in self.student1:# this is added as there are duplicate entries in the student list if saved
if i not in self.student2:
self.student2.append(i)
self.parent.destroy()
lis_an(self.student2,self.ng1)
def save_list2(self):
for entry in self.student:
self.student1.append(entry.get())
self.student1 = filter(None, self.student1)
import tkFileDialog
root = Tk()
root.withdraw()
file_name = tkFileDialog.asksaveasfile(parent=root)
root.destroy()
print >>file_name, "\n"
print >>file_name, "\n".join(self.student1)
file_name.close()
class lis_an:
def __init__(self,student1,ng1):
self.student1 = student1
self.ng1=ng1
self.results = []
self.randomList()
def randomList(self): # this creates a random list of students on the course
import random
studentb = self.student1[:]# this is added as otherwise the student list is overwritten
studentc = []
for i in range(len(studentb)):
element = random.choice(studentb)
studentb.remove(element)
studentc.append(element)
self.student1 = studentc
self.partition()
def partition(self): # this creates sub list of the student list containing the groups of students
increment = len(self.student1) / float(self.ng1)
last = 0
i = 1
while last < len(self.student1):
idx = int(round(increment * i))
self.results.append(self.student1[last:idx])
last = idx
i += 1
output(self.results, self.ng1)
class output:
def __init__(self, student, ng1):
self.ng1 = ng1
self.student = student
self.parent = Tk()
for item1 in range (0,len(self.student)):
test1 = "Group " + str(item1+1)+ ":"
v = Label(self.parent, text=test1, font=("Arial", 13))
test = "\n".join(self.student[item1])
w = Label(self.parent, text=test, justify = LEFT, font=("Arial", 12))
v.pack(side="top", anchor="w")
w.pack(side="top", anchor="w")
button=Button(self.parent,text="Repeat",command=lambda : self.join_list()).pack(side="top", anchor="w")
button=Button(self.parent,text="Main Menu",command=lambda : self.menu_link()).pack(side="top", anchor="w")
mainloop()
def join_list(self):#this function creates a new undivided version of student to feed back to lis_an
self.parent.destroy()
self.student = [j for i in self.student for j in i]
lis_an(self.student,self.ng1)
def menu_link(self):#destroys the parent frame and returns back to main menu
self.parent.destroy()
main()
def main():
parent = Tk()
myapp = main_menu(parent)
parent.mainloop()
main()
Very Simple solution to this problem. I was not defining self.ngg as a StringVar in the parent widget so:
self.ngg = StringVar()
Would cause the error, and
self.ngg = StringVar(self.app)
Solves the problem. Not quite sure why this would occur on the second and subsequent uses of the function but not the first.

Categories

Resources