This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 1 year ago.
I'm going to ask the user to enter a number in an entry and press the 'enter' button, then show that number of entries below on the window, here is my code, the test class makes a list of entries and it works well alone but when I combine it with the first scenario and press the button nothing happens:
from tkinter import *
root = Tk()
root.geometry("600x600+50+50")
font = ('Verdana', 10)
def enter():
try:
class test:
def __init__(self, root):
self.variables = []
n = eval(txt_num.get())
for i in range(n):
self.variables.append(StringVar())
self.labels = []
self.entrys = []
mod = n % 4
c = 0
d = 0
e = 0
for ii in range(int(n/4)):
for jj in range(4):
char = c+(jj+1)
self.labels.append(Label(root , text = char))
self.labels[-1].grid(padx=0, pady=0, row=ii+d+1, column=jj)
self.entrys.append(Entry(root, textvariable =self.variables[c+(jj+1)]))
self.entrys[-1].grid(padx=0, pady=0, row=ii+d+2, column=jj)
d+=2
c+=4
e = ii+d+1
for kk in range(mod):
char = c+(kk+1)
self.labels.append(Label(root , text = char))
self.labels[-1].grid(padx=0, pady=0, row=int(n/4)+1+e+1, column=kk)
self.entrys.append(Entry(root, textvariable =self.variables[c+(kk)]))
self.entrys[-1].grid(padx=0, pady=0, row=int(n/4)+1+e+2, column=kk)
T = test(root)
except:
pass
lable_num = Label(font = font, text = 'Enter the number of your numbers:')
lable_num.grid(row = 0 , column = 0, columnspan = 3)
txt_num = Entry(width = 15, font = font, fg = 'gray' )
txt_num.grid(row = 0 , column = 4)
button_enter = Button(text = 'Enter' , font = font, command = enter())
button_enter.grid(row = 0 , column = 5)
root.mainloop()
The problem was calling the function command = enter() wrongly, since I have to pass the function itself as the command= option; it should be command = enter.
Related
I'd like to switch from the rating_frame to the summary_frame. How would I do this? Would I destroy the rating_frame? I want to go onto the rating_frame by clicking the 'Show' button.
I have a search frame that is staying there. I only want the rating frame to change.
I have not yet made a start on the summary_frame as I don't know how to change from the rating_frame or where I would write it. Could you give me a good foundation?
Here is my Wireframe:
Here is my code:
from tkinter import *
class Movie:
def __init__(self, movie):
self.movie = movie
self.ratings = "No Rating"
class MovieRaterGUI:
def __init__(self, parent):
self.counter = 0
self.index = 0
#variable set up
self.v = StringVar()
self.v.set("No Rating")
#frames used so you can easily switch between rating frame and summary frame - keeping the search frame
rating_frame = Frame(root)
search_frame = Frame(root)
summary_frame = Frame(root)
rating_frame.pack(side="top", expand=True)
search_frame.pack(side="bottom", expand=True)
summary_frame.pack(side="top", expand = True)
#rating frame
#list of ratings for movies
self.movies = [
Movie("The Hobbit"),
Movie("Coraline"),
Movie("Love, Rosie")]
#used to display the ratings
self.ratings = ["No Rating", "Forget it", "2", "3", "4", "Must See"]
#labels
self.movie_label = Label(rating_frame, text = "Please Rate:", borderwidth = 10)
self.current_movie = Label(rating_frame, text = self.movies[self.counter].movie, borderwidth = 10)
self.rating_label = Label(rating_frame, text = "Your rating:", borderwidth = 10)
self.movie_label.grid(row = 0, column = 0, sticky = W)
self.current_movie.grid(row = 0, column = 1, sticky = W)
self.rating_label.grid(row = 1, column = 0, sticky = W)
#making radio buttons
self.radiobutton = []
self.num_choices = self.ratings
for i in range(len(self.ratings)):
self.option = Radiobutton(rating_frame, variable = self.v, value = self.ratings[i], text = self.ratings[i], borderwidth = 10, command = self.update_rating)
self.radiobutton.append(self.option)
self.option.grid(row = i+1, column = 1, sticky = W)
next_btn = Button(rating_frame, text = "Next", borderwidth = 10, command = self.next_movie)
previous_btn = Button(rating_frame, text = "Previous", borderwidth = 10, command = self.previous_movie)
next_btn.grid(row = 7, column = 1, sticky = W)
previous_btn.grid(row = 7, column = 0, sticky = W)
#search frame
self.search_label = Label(search_frame, text = "Search for movies with a rating of:", borderwidth = 10)
self.search_label.grid(row=0, column=0, columnspan=len(self.num_choices))
for i in range(len(self.num_choices)):
option = Radiobutton(search_frame, variable = self.v, value = i, text = self.num_choices[i])
option.grid(row = 1, column = i, sticky = W)
show_btn = Button(search_frame, text = "Show", borderwidth = 10, command = self.summary_frame)
show_btn.grid(row = 3, column = 0, columnspan = len(self.num_choices))
def next_movie(self):
self.counter +=1
self.current_movie.configure(text = self.movies[self.counter].movie)
#used so each radio button the user chooses will be saved
for i in range(len(self.radiobutton)):
self.radiobutton[i].configure(variable = self.v, text = self.ratings[i], value = self.ratings[i])
#the default movie rating is no rating for every movie
self.v.set("No Rating")
def previous_movie(self):
self.counter -=1
self.current_movie.configure(text = self.movies[self.counter].movie)
#the default movie rating is no rating for every movie
self.v.set("No Rating")
def update_rating(self):
self.movies[self.counter].ratings = self.v.get()
for element in self.movies:
print(element.ratings)
print()
print('*'*20)
print()
if __name__ == "__main__":
root = Tk()
root.title("Movie Ratings")
radiobuttons = MovieRaterGUI(root)
root.mainloop()
If you use destroy then you destroy also data which you have in this frame - and you couldn't use them. You would have to get data from frame before you destroy it.
But it may be better to remove frame without destroying (pack_forget, grid_forget) and then you have access to data in frame and you can always display this frame again.
With grid it can be simpler to put new element in the same place.
Where put this code?
Usually programs have buttons << Previous,Next >> or << Details, Summary >> to change frames/steps - but it seems you forgot these buttons.
Eventually you can use Notebook to have frames in tabs.
Applications for smartphones usually can slide frames (using fingers) but desktop programs rather don't use this method. And tkinter doesn't have special methods for this. It would need much more code with events or drag'&'drop. Buttons are much simpler to create and simpler to use by users.
These are few lines from my actual code - I am aware this is not the best way of writing a code, but as I am new and getting familiarize with Tkinter (py2) consider this as my scratch work.
I am listing a question and multiple options. When the user selects an option, a SUBMIT button is created and when clicks on SUBMIT button it will accordingly change the color of Option to green or red. If green then another NEXT button will be available to clean and move to next question.
The issue that I am facing is if a user selects option A but then without clicking the SUBMIT button selects another option the submit button multiplies. I want to destroy the unwanted buttons or even do not want to create multiple SUBMIT buttons.
Please do help in achieving the same.
import Tkinter
from Tkinter import *
import yaml
import random
grey = "#808080"
offwhite = "#e3e3e3"
filepath = "chapter-2.yaml"
tk = Tkinter.Tk()
tk.title("iCodet Learnings")
tk.geometry("800x600")
x = ''
tk.config(background=offwhite)
tk.resizable(0,0)
q_count = 0
def yaml_loader(filepath):
with open (filepath, "r") as fileread:
data = yaml.load(fileread)
return data
def cleaner(hint):
global rbutton
global q_count
global quest_label
global radio1
global button_game
quest_label.destroy()
radio1.destroy()
# destroys the radio buttons
for b in rbutton:
b.destroy()
# destroys the SUBMIT button
button_game.destroy()
# go to ext question
if hint == 'next':
q_count += 1
game_loop()
# This is display the first element from the yaml i.e the question
def display_question(questions, qc):
global quest_label
q = questions.keys()[qc]
a = questions[q]
v = a.keys()
quest_label = Label(tk, text = q, font = ("Consolas", 16), width = 500, justify = "center", wraplength = 400)
quest_label.pack(pady = (50,0))
return v
# This is for selecting the radio buttons
def selected():
global radio_default, button_next,radio1, val
global x, data,q_count, vali, rbutton, select_val
x = radio_default.get()
select_val = rbutton[x]
if q_count <= len(data):
q = data.keys()[q_count]
a = data[q] #second dictionary
v = a.keys() #second dictionary keys
# True or False from Yaml
val = a[v[x]][0]
press_button(val)
else:
print ("Mid way")
# This will list all the options under question
def display_answer(ans):
global radio1, rbutton
global x, q_count
global radio_default
radio_default = IntVar()
rbutton = []
rad_select = []
val_count = 0
for i in ans:
radio1 = Radiobutton(tk, text = i, font = ("times", 14, "bold"), value = val_count, variable = radio_default, command = selected, background = 'NavajoWhite3')
rbutton.append(radio1)
val_count += 1
radio1.pack(pady = (30,0))
radio_default.set(-1)
# This displays the SUBMIT buuton
def press_button(val):
global button_game
# true
if val:
button_game = Button(tk, text = 'SUBMIT', font = ("default", 15, "bold"), bg='orange', fg = 'white', border=2, height = 2, width = 8, command = lambda: cleaner('next'))
button_game.pack(pady = (30,0))
# false
elif not val:
print "Do nothing"
button_game = Button(tk, text = 'SUBMIT', font = ("default", 15, "bold"), bg='orange', fg = 'white', border=2, height = 2, width = 8, command = lambda: cleaner('stay'))
button_game.pack(pady = (30,0))
return True
def game_loop():
global q_count
global x, data
global quest_label, button_game
action = True
data = yaml_loader(filepath)
if q_count <= len(data)-1:
l_ans = display_question(data, q_count)
display_answer(l_ans)
else:
txt_label = Label(tk, text = "CONGRATULATIONS ON COMPLETING CHAPTER", font = ("Comicsans", 24, "bold"), background = offwhite, wraplength = 700)
txt_label.pack(pady = (100,0))
button_end = Button(tk, text = 'THANK YOU !', font = ("default", 15, "bold"), bg='saddle brown', fg = 'white', border=2, height = 3, width = 10, command = tk.destroy)
button_end.pack(pady = (50,0))
game_loop()
tk.mainloop()
chapter-1.yaml
> "What’s the complete name of Sachin Tendulkar ?":
> "Sachin Ramya Tendulkar":
> - False
> "Sachin Ramesh Tendulkar":
> - True
> "Sachin Tendehar":
> - False
> " Sachin 10dulkar":
> - False
> "Hint":
> - "biscuit & cookies"
As things are, each time press_button() is run, a new Button object is generated, and placed in the button_game variable. This does not remove or hide the previous button, which still exists in the packed UI.
A simple solution that would save the machine some work is to initialize the button only once, earlier in the code, but omit placing/displaying/packing it until that block within press_button() is run.
I was able to achieve what I was looking for with the help of config.
I created the SUBMIT button once at the beginning and then instead of calling the whole function again and again; I just replaced press_button with button_game.config(command = lambda: right(chapter, num_ques, topic, val))
Now I should write this code using class in python.
I have a problem with the code and an error message appears in word -> word = "" word = str (Entry.get (input_text)) which obviously refers to printing full text which does not work the way I want it when I want it to work in a non-root window window but reading window.
This is the codes :
from tkinter import *
import sys
import tkinter as tk
from tkinter import messagebox
#---------
root = Tk()
root.title("Window")
#----------
#toiminnot root ikkunassa
functions = Label(root, text = "Select an action!")
functions.place(x = 70, y= 10)
#lue toimonto koodi alkaa
def read():
reading_window = Tk()
reading_window.title("Read function")
frame = Frame(reading_window)
frame.pack()
read_the_text = Label(reading_window, text = "Enter text in the box!")
read_the_text.place(x = 70, y = 10)
word = ""
word = str(Entry.get(input_text))
#frame johon kirjoitetaan
input_text = Entry(reading_window)
input_text.place(x=55, y=30)
#lueikkuna koko ja sijoitus
reading_window.geometry("300x300+100+100")
#lue sana painike joka tuo viestin
print_button = tk.Button(reading_window, text = 'Show typed text', height = 1, width = 15) #, command = print1)
print_button.place(x=80, y=60)
text_a_tip = Label(reading_window, text ="The typed text is displayed in\nthe message window!")
text_a_tip.place(x = 50, y = 90)
def print1():
tk.messagebox.showinfo('Kirjoitetun tekstin tulostus viestiikkunaan', (Entry.get(input_text)))
def close():
reading_window.destroy()
read_close_button = tk.Button(reading_window, text = 'Close the reading function', height = 1, width = 20, command = close)
read_close_button.place(x = 60, y = 270)
read.mainloop()
#lue toiminto koodi loppuu
read_function = tk.Button(root, text='Read function', height = 1, width = 15, command = read)
read_function.place(x = 55,y = 35)
#ohjleman lopettamisen koodi alkaa
def quit_prog():
MsgBox = tk.messagebox.askquestion('Quit program', ' Are you sure you want to close the program?',icon = 'warning')
if MsgBox == 'yes':
root.destroy()
sys.exit(0)
else:
tk.messagebox.showinfo('Back','Now you are back!')
quit_programbutton = tk.Button(root, text='Close program', height = 1, width = 15, command = quit_prog)
quit_programbutton.place(x=50, y=220)
#ohjelman lopettamisen koodi loppuu tähän
#----------
#----------
root.geometry("250x250+20+60") #"450x450=leveysxkorkeus"+"20+40=vasenreuna+yläreuna"
root.mainloop()
source
I don't really see what is "word" variable for, but I assume that You need it. You can't use variable defined under line in which you use it.
word = ""
word = str(Entry.get(input_text)) # input_text is defined below, interpreter doesn't know what is it "input_text" yet.
input_text = Entry(reading_window)
input_text.place(x=55, y=30)
It should be rather.
input_text = Entry(reading_window)
input_text.place(x=55, y=30)
word = ""
word = str(Entry.get(input_text)) # now there is no error
It doesn't solve your problem, because I see that in function print1 you use again imput_text but it's defined in different function so it won't work.
def print1():
# input_text is local variable in read function.
tk.messagebox.showinfo('Kirjoitetun tekstin tulostus viestiikkunaan', (Entry.get(input_text)))
To solve your problem you can pass input_text as argument of print1, like in this working example.
def read():
reading_window = Tk()
reading_window.title("Read function")
frame = Frame(reading_window)
frame.pack()
read_the_text = Label(reading_window, text = "Enter text in the box!")
read_the_text.place(x = 70, y = 10)
input_text = Entry(reading_window)
input_text.place(x=55, y=30)
word = ""
word = str(Entry.get(input_text))
reading_window.geometry("300x300+100+100")
# pass input_text to print1
print_button = tk.Button(reading_window, text = 'Show typed text', height = 1, width = 15, command = lambda: print1(input_text))
print_button.place(x=80, y=60)
text_a_tip = Label(reading_window, text ="The typed text is displayed in\nthe message window!")
text_a_tip.place(x = 50, y = 90)
def print1(input_text):
# print1 takes now one argument and has reference to input_text
tk.messagebox.showinfo('Kirjoitetun tekstin tulostus viestiikkunaan', (Entry.get(input_text)))
def close():
reading_window.destroy()
read_close_button = tk.Button(reading_window, text = 'Close the reading function', height = 1, width = 20, command = close)
read_close_button.place(x = 60, y = 270)
read.mainloop()
Consider also using different geometry manager than place, it is even written in effbot doc:
It is usually not a good idea to use place for ordinary window and dialog layouts; its simply to much work to get things working as they should. Use the pack or grid managers for such purposes.
Place Geometry Manager
I am very new to Tkinter ( I find it very difficult to learn). I have a python script working based on user input. I would like to wrap a GUI around it and eventually put it on web. In any case for user input I would like to get this from the GUI with a combination of Entry widgets and some buttons. First thing is I was reading and some people mentioned to use a class so I have the following. I have a few questions
I would like to check to see if indeed the users entered a value before he hits the GO button. How do I do this?
I would like the value entered to be made accessible by the rest of the program in the main body. How do I do this?
Thanks,
from Tkinter import *
class MainWindow():
def get_fc(self):
a = self.fc_gui.get()
return a
def __init__(self, master):
self.master = master
self.master.title('TEST')
self.fc_gui = DoubleVar(self.master, value = 500.00)
self.fclabel1 = Label(self.master, text = 'Please Enter a value', fg = 'black', bg = 'yellow')
self.fclabel1.grid(row = 0, column = 0)
self.fcedit1 = Entry(self.master, textvariable = self.fc_gui, bd = 5 )
self.fcedit1.grid(row = 1, column = 0)
fcbutton1 = Button(self.master, text='GO', command = self.get_fc)
fcbutton1.grid(row = 1, column = 1)
master = Tk()
MainWindow(master)
master.mainloop()
It doesn't make sense to return to a Button. The Button can't do anything with the value. Instead, save the value as an instance variable.
You don't have a mainloop().
You can't really check if the user entered a value before they hit "Go" - at the start of the program, of course they haven't entered anything yet. If you needed to track the contents of this field, there are ways to do that, but it's not necessary for a simple validation. Just check the value when they hit the button.
from Tkinter import *
class MainWindow():
def get_fc(self):
a = self.fc_gui.get()
if a: # this block will execute if a has content
self.a = a # save it for future use
def __init__(self, master):
self.master = master
self.master.title('TEST')
self.fc_gui = DoubleVar(self.master, value = 500.00)
self.fclabel1 = Label(self.master, text='Please Enter a value',
fg = 'black', bg = 'yellow')
self.fclabel1.grid(row = 0, column = 0)
self.fcedit1 = Entry(self.master, textvariable = self.fc_gui, bd = 5 )
self.fcedit1.grid(row = 1, column = 0)
fcbutton1 = Button(self.master, text='GO', command = self.get_fc)
fcbutton1.grid(row = 1, column = 1)
master = Tk()
MainWindow(master)
master.mainloop() # don't forget mainloop()
This is the code for the function I'm using to start the main part of the program, however I want some sort of loop or something which creates ten questions, but waits for an input from the Entry box before moving onto the next question.
Any ideas?
def StartGame():
root = Tk()
root.title("Maths Quiz - Trigonometry and Pythagoras' Theorem | Start The Game")
root.geometry("640x480")
root.configure(background = "gray92")
global AnswerEntry
TotScore = 0
Count = 0
AnswerReply = None
WorkingArea = Text(root, width = 70, height = 10, wrap = WORD).place(x = 38, y = 100)
n = GetRandomNumber()
Angle,Opposite,Adjacent,Hypotenuse = Triangle()
Question,RealAnswer = QuestionLibrary(Opposite,Adjacent,Hypotenuse,Angle,n)
AskQuestion = Label(root, text = Question, wraplength = 560).place(x = 48, y = 300)
PauseButton = ttk.Button(root, text = "Pause").place(x = 380, y = 10)
HelpButton = ttk.Button(root, text = "Help", command = helpbutton_click).place(x = 460, y = 10)
QuitButton = ttk.Button(root, text = "Quit", command = root.destroy).place(x = 540, y = 10)
AnswerEntry = Entry(root)
AnswerEntry.place(x = 252, y = 375)
SubmitButton = ttk.Button(root, text = "Submit", command = submit_answer).place(x = 276, y = 400)
TotScore,AnswerReply = IsAnswerCorrect(Answer,RealAnswer)
ScoreLabel = ttk.Label(root, text = TotScore).place(x = 38, y = 10)
AnswerReplyLabel = ttk.Label(root, text = AnswerReply).place(x = 295, y = 440)
root.mainloop()
I want the loop to start after the AnswerReply = None
You don't want a loop. The only really important loop inside a GUI should be the mainloop(), handling signal and executing callbacks.
Example:
try:
import Tkinter as Tk
except ImportError:
import tkinter as Tk
class QAGame(Tk.Tk):
def __init__(self, questions, answers, *args, **kwargs):
Tk.Tk.__init__(self, *args, **kwargs)
self.title("Questions and answers game")
self._setup_gui()
self._questions = questions[:]
self._answers = answers
self._show_next_question()
def _setup_gui(self):
self._label_value = Tk.StringVar()
self._label = Tk.Label(textvariable=self._label_value)
self._label.pack()
self._entry_value = Tk.StringVar()
self._entry = Tk.Entry(textvariable=self._entry_value)
self._entry.pack()
self._button = Tk.Button(text="Next", command=self._move_next)
self._button.pack()
def _show_next_question(self):
q = self._questions.pop(0)
self._label_value.set(str(q))
def _move_next(self):
self._read_answer()
if len(self._questions) > 0:
self._show_next_question()
self._entry_value.set("")
else:
self.quit()
self.destroy()
def _read_answer(self):
answer = self._entry_value.get()
self._answers.append(answer)
def _button_classification_callback(self, args, class_idx):
self._classification_callback(args, self._classes[class_idx])
self.classify_next_plot()
if __name__ == "__main__":
questions = ["How old are you?",
"What is your name?"]
answers = []
root = QAGame(questions, answers)
root.mainloop()
for q,a in zip(questions, answers):
print "%s\n>>> %s" % (q, a)
We only have a Label, an Entry and a Button (I did not care about layout!, just pack()).
Attached to the button is a command (aka callback). When the button is pressed, the answer is read and the new question is assigned to the label.
Usage of this class is understandable from the example in the `if name == "main" block. Please note: the answers-list is filled in place, the questions-list is kept unchanged.
I don't know Tk, but is there no any signals of input text changed? There should be for sure. Just check if this signal occured and then move onto new question, because it means that someone typed something in input box.