Python - tkinter interface, using bind works only the first time (when running the program) - python-3.x

I'm attaching the code below.
There is an event on "focus out", the event triggers once when the code is initialized. Afterwards focusing out of the Spinbox does not trigger the event anymore. The question is, why? And, how do I fix that?
code :
import tkinter as tk
import os, time
def spinBoxValidateRange(widget_name):
print(str(widget_name.winfo_name()) + " focus out")
# Making a window, and giving it some settings
root = tk.Tk()
root.resizable(False, False)
root.winfo_toplevel().title("Test")
# creating a user GUI
default_pady = 2
default_padx = 5
sbx_max_img_width = tk.Spinbox(from_=500, to=5000, width = 4)
sbx_max_img_width.delete(0, 'end')
sbx_max_img_width.insert(0, 1000)
sbx_max_img_height = tk.Spinbox(from_=500, to=5000, width = 4)
sbx_max_img_height.delete(0, 'end')
sbx_max_img_height.insert(0, 1000)
sbx_max_img_width.grid(row = 0, column = 1, sticky = "W", pady = default_pady, padx = default_padx)
sbx_max_img_height.grid(row = 0, column = 3, sticky = "W", pady = default_pady, padx = default_padx)
sbx_max_img_width.bind("<FocusOut>", spinBoxValidateRange(sbx_max_img_width))
sbx_max_img_height.bind("<FocusOut>", spinBoxValidateRange(sbx_max_img_height))
root.mainloop()

Here is the corrected code (that works) -
import tkinter as tk
import os, time
def spinBoxValidateRange(some_widget):
print(str(some_widget.widget.winfo_name()) + " focus out")
# Making a window, and giving it some settings
root = tk.Tk()
root.resizable(False, False)
root.winfo_toplevel().title("Test")
# creating a user GUI
default_pady = 2
default_padx = 5
sbx_max_img_width = tk.Spinbox(from_=500, to=5000, width = 4)
sbx_max_img_width.delete(0, 'end')
sbx_max_img_width.insert(0, 1000)
sbx_max_img_height = tk.Spinbox(from_=500, to=5000, width = 4)
sbx_max_img_height.delete(0, 'end')
sbx_max_img_height.insert(0, 1000)
sbx_max_img_width.grid(row = 0, column = 1, sticky = "W", pady = default_pady, padx = default_padx)
sbx_max_img_height.grid(row = 0, column = 3, sticky = "W", pady = default_pady, padx = default_padx)
sbx_max_img_width.bind("<FocusOut>", lambda parameter = sbx_max_img_width: spinBoxValidateRange(parameter))
sbx_max_img_height.bind("<FocusOut>", lambda parameter = sbx_max_img_height: spinBoxValidateRange(parameter))
root.mainloop()

Related

I want my code to wait unless either of two buttons is pressed

ui.py
import tkinter as tk
sc = 0
q_txt = 'Can you please verify the pythagorous theorem using similarities of triangle.'
class Window():
var = tk.IntVar()
def __init__(self):
self.window = tk.Tk()
self.window.title('Guess if you can')
self.window.config(padx = 50, pady = 50, bg = '#130f4a')
self.window.minsize(width = 400, height = 300)
self.score = tk.Label(text = 'score: 0',fg = 'white' ,bg = "#130f4a", font = ('Ariel', 12, 'bold'))
self.score.grid(row = 0, column = 1)
self.true = tk.PhotoImage(file = '_tick.png')
self.false = tk.PhotoImage(file = '_cross.png')
self.cnvs = tk.Canvas(self.window, width = 300, height= 250, bg = 'white', highlightthickness = 0)
self.cnvs_txt = self.cnvs.create_text(300//2, 250//2, text = q_txt, font = ('Times 20 italic bold', 15), width = 200)
self.cnvs.grid(row = 1, column = 0, columnspan = 2, pady = 20)
V = tk.IntVar(0)
self.tick_btn = tk.Button(self.window, image = self.true, highlightthickness = 0, bg = 'green', command = lambda : V.set(1))
self.tick_btn.grid(row = 2, column = 0, pady = 5)
self.cross_btn = tk.Button(self.window, image = self.false, highlightthickness = 0, bg = 'red')
self.cross_btn.grid(row = 2, column = 1, pady =5)
def change_question(self, next_question):
self.cnvs.itemconfig(self.cnvs_txt, text = next_question)
def asktopress(self, V):
self.tick_btn.wait_variable(V)
self.window.mainloop()
main.py
import questions
import ui
QA = questions.que_and_ans
ob = ui.Window()
next_question = 'My name is anthony'
ob.asktopress()
ob.change_question(next_question)
questions.py
import json
with open('question_bank.txt') as file:
f = file.read()
data = json.loads(f)
que_and_ans = [(x['question'], x['correct_answer']) for x in data['results']]
Stuck at
So, basically I want to keep changing the question after either one of two buttons is pressed
Let say,
There is question#1 (boolean question True or False type only) and now the execution should have to wait unless and until tick_btn or cross_btn is pressed so depending on right or wrong it updates the score the next I think I'll be able to do but I'm stuck with bold sentence.
My search
I went through thread#1
python documentation
#1StOverflow
isClicked
The one I tried to implement
Error I'm facing
Traceback (most recent call last):
File "C:\Users\alphaowner\Desktop\#100_Days_of_Python\#34-Day\Project\main.py", line 2, in <module>
import ui
File "C:\Users\alphaowner\Desktop\#100_Days_of_Python\#34-Day\Project\ui.py", line 4, in <module>
class Window():
File "C:\Users\alphaowner\Desktop\#100_Days_of_Python\#34-Day\Project\ui.py", line 5, in Window
var = tk.IntVar()
File "C:\Users\alphaowner\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 539, in __init__
Variable.__init__(self, master, value, name)
File "C:\Users\alphaowner\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 346, in __init__
master = _get_default_root('create variable')
File "C:\Users\alphaowner\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 297, in _get_default_root
raise RuntimeError(f"Too early to {what}: no default root window")
RuntimeError: Too early to create variable: no default root window
The error you are receiving is because in ui.py, you are initializing/creating a Tkinter object/variable of IntVar() type, var = tk.IntVar(), before the tk.Tk() root is initialized, so the solution is:
class Window():
def __init__(self):
self.window = tk.Tk()
var = tk.IntVar()
self.window.title('Guess if you can')
Now, the question is what way do you want the execution of the program
to wait until tick_btn or cross_btn is pressed?
You don't need to take care of the program to wait, because it will itself wait until the user makes any move (presses cross, or presses tick, or closes the program.)
See, when the Tkinter window is in mainloop, its execution has started and now all you need to do is, change every question (as well as the score) depending upon what button user presses.
So, just call a function for both the buttons and in that function, check the answer, update the score, ask new question, and also display the result.
self.tick_btn = tk.Button(self.window, image = self.true, highlightthickness = 0, bg = 'white', command = lambda:self.check_answer(True))
self.tick_btn.grid(row = 2, column = 0, pady = 5)
self.cross_btn = tk.Button(self.window, image = self.false, highlightthickness = 0, bg = 'white', command = lambda:self.check_answer(False))
self.cross_btn.grid(row = 2, column = 1, pady =5)
self.window.mainloop()
def check_answer(self, val):
#all questions done!
if self.questionNo >= len(self.QA):
print("Done!")
else:
if (val == True and self.answer == "true") or (val == False and self.answer == "false"):
print("Correct")
self.var.set(self.var.get()+1) #works as self.var+=1
self.score["text"] = 'Score: '+str(self.var.get())
else:
#negative marking or do nothing
print("Wrong")
#Now move to next question
self.questionNo+=1
if self.questionNo < len(self.QA):
self.q_txt = self.QA[self.questionNo][0]
self.answer = self.QA[self.questionNo][1]
self.cnvs.itemconfig(self.cnvs_txt, text = self.q_txt)
else: #last question done
print("Your final score is "+str(self.var.get()))
Here is a link to my GitHub repo, I made changes to your code to create a True-False QnA program using Tkinter.

How can I scroll multiple frames in canvas?

I want to create a list of frames with further features like a button, label e.g.. but my issues are the size of the LabelFrame. If I put the LabelFrame in container it fits like I want to but it isn't scrollable any more. Any ideas?
from tkinter import ttk
import tkinter as tk
root = tk.Tk()
container = ttk.Frame(root)
canvas = tk.Canvas(container)
scrollbar = ttk.Scrollbar(container, orient="vertical", command=canvas.yview)
scrollable_frame = ttk.Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(
scrollregion=canvas.bbox("all")
)
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
for i in range(50):
lf = ttk.Frame(scrollable_frame, text=i).grid(column=1, row=i)
frame_ip = tk.LabelFrame(lf, bg="white", text=i)
frame_ip.place(relwidth=0.95, relheight=0.2, relx=0.025, rely=0)
button_scroll1 = tk.Button(frame_ip, text="Start", bg="grey")
button_scroll1.place(relwidth=0.15, relx=0.025, relheight=0.15, rely=0.1)
container.pack()
canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
root.mainloop()
Here is your updated code with a Button, Canvas and Scrollbar inserted into each LabelFrame with grid manager.
I've also made the container resizable with row|column configure.
Seems to work fine.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.rowconfigure(0, weight = 1)
root.columnconfigure(0, weight = 1)
root.geometry("341x448")
container = ttk.Frame(root)
container.rowconfigure(0, weight = 1)
container.columnconfigure(0, weight = 1)
canvas = tk.Canvas(container)
scrollbar = ttk.Scrollbar(container, orient = tk.VERTICAL, command = canvas.yview)
scrollable_frame = ttk.Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(
scrollregion=canvas.bbox("all")))
canvas.create_window((0, 0), window = scrollable_frame, anchor = tk.NW)
canvas.configure(yscrollcommand = scrollbar.set)
for i in range(15):
L = ttk.LabelFrame(scrollable_frame, text = "Sample scrolling label")
L.grid(row = i, column = 0, sticky = tk.NSEW)
B = ttk.Button( L, text = f"Button {i}")
B.grid(row = 0, column = 0, sticky = tk.NW)
K = tk.Canvas(
L, width = 300, height = 100,
scrollregion = "0 0 400 400", background = "#ffffff")
K.grid(row = 1, column = 0, sticky = tk.NSEW)
S = ttk.Scrollbar( L, command = K.yview)
S.grid(column = 1, row = 1, sticky = tk.NSEW)
K["yscrollcommand"] = S.set
container.grid(row = 0, column = 0, sticky = tk.NSEW)
canvas.grid(row = 0, column = 0, sticky = tk.NSEW)
scrollbar.grid(row = 0, column = 1, sticky = tk.NS)
root.mainloop()

Python tkinter - label not showing on the 2nd screen

I created a code with a yes/no question, and if yes, I use an entry box to ask how many. But when I reach to that How many question, the label is not showing and I don't understand why?
Thanks in advance, below is the code:
from tkinter import filedialog, messagebox, ttk, constants
from tkinter import *
root = Tk()
root.focus_force()
root.withdraw()
root.call('wm', 'attributes', '.', '-topmost', True)
yesnob = messagebox.askyesno('Test','Do you have a clue?')
if yesnob == True:
root2 = Tk()
root2.call('wm', 'attributes', '.', '-topmost', True)
root2.wm_title('How many ?')
nb_b = 0
title_loop = Label(root2, textvariable = 'How many ?', height = 2, width = 15)
title_loop.grid(row = 1, column = 0)
entrybox = Entry(root2, textvariable = nb_b, width = 5)
entrybox.grid(row = 2, column = 0)
def get_data():
global nb_b
try:
nb_b = int((entrybox.get()))
except ValueError:
no_int = messagebox.showerror('Error', 'You did not enter a number, try again!')
root.destroy()
root2.destroy()
exit_but = Button(root2, text = 'OK', command = get_data, height = 3, width = 5)
exit_but.grid(row = 3, column = 1)
root2.mainloop()
else:
root.destroy()
root.mainloop()
Changing the "textvariable" to "text" worked for me:
title_loop = Label(root2, text = 'How many ?', height = 2, width = 15)
You created the Label with the textvariable argument. If you change it to text the label is shown:
title_loop = Label(root2, text= 'How many ?', height = 2, width = 15)
textvariable can be used in combination with a StringVar if you want to have a text that can be changed. If the text is static use the text argument.

I want to print my all excel data in new tkinter window using python

I would like print all the information present in the excel data in new tkinter window
You can use Tkinter's grid.
For example, to create a simple excel-like table:
from Tkinter import *
root = Tk()
height = 5
width = 5
for i in range(height): #Rows
for j in range(width): #Columns
b = Entry(root, text="")
b.grid(row=i, column=j)
mainloop()
To print, consider the following example in which I make a button with Tkinter that gets some text from a widget and then prints it to console using the print() function.
from tkinter import *
from tkinter import ttk
def print_text(*args):
try:
print(text1.get())
except ValueError:
pass
root = Tk()
root.title("Little tkinter app for printing")
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column = 0, row = 0, sticky = (N,W,E,S))
mainframe.columnconfigure(0, weight = 1)
mainframe.rowconfigure(0, weight = 1)
text1 = StringVar()
text_entry = ttk.Entry(mainframe, width = 20, textvariable=text1)
text_entry.grid(column = 1, row = 2, sticky = (N,W,E,S))
ttk.Button(mainframe, text = "Print!", command =
print_text(text1)).grid(column = 1, row = 3, sticky = (E))
for child in mainframe.winfo_children():
child.grid_configure(padx = 5, pady = 5)
text_entry.focus()
root.bind('<Return>', print_text)
root.mainloop()

Cell formatting in Grid Geometry manager

I have this code:
from tkinter import *
from tkinter import ttk
class Application:
def __init__(self, master):#Change to game class when combining code
self.master = master#remove when combining code
self.frame_Canvas = ttk.Frame(self.master, width = 600, height = 600)
self.frame_Canvas.pack(side = 'left')
self.frame_Canvas.pack_propagate(False)
self.frame_Canvas.grid_propagate(False)
self.hangman = Canvas(self.frame_Canvas, width = 600, height = 600,
background = 'white').pack()
self.FullName = ttk.Label(self.frame_Canvas, text = "Full Name", background = 'white')#full name will be entered here
self.FullName.config(font=("TkDefaultFont", 20))
self.FullName.place(x = 10, y = 10)
self.frame_Interact = ttk.Frame(self.master, width = 200, height = 600)
self.frame_Interact.pack(side = 'right')
self.frame_Interact.pack_propagate(False)
self.frame_Interact.grid_propagate(False)
self.QuestionLabel = ttk.Label(self.frame_Interact, text = "Question:")
self.QuestionLabel.config(font=("TkDefaultFont", 20))
self.QuestionLabel.grid(column = 0, row = 0)
self.QuestionShow = Text(self.frame_Interact, height=1, width=8)#input question here
self.QuestionShow.config(font=("TkDefaultFont", 20))
self.QuestionShow.grid(column = 0, row = 1)#FIX THE FORMATTING OF QUESTION, GRID CELL TO LEFT, NOT BIG ENOUGTH?
self.AnswerEntry = ttk.Entry(self.frame_Interact, width = 10)#do later
def main():
root = Tk()
root.wm_title("Hangman")
Menu = Application(root)
root.resizable(width=False, height=False)
root.iconbitmap("windowicon.ico")
root.mainloop()
if __name__ == "__main__": main()
I dont know why, but the Answer box and label on the right side of my tkinter GUI is on the left side of its frame. I want it in the center. Does anyone know a way to frix it, or any improvements for the code so far. Thanks :)
There are two ways you can do this, first way will to be to use .pack() instead of .grid() since pack is very easy to use and does the aligning for you automatically.
So you can just replace:
self.QuestionLabel.grid(column = 0, row = 0)
# and
self.QuestionShow.grid(column = 0, row = 1)
With:
self.QuestionLabel.pack()
# and
self.QuestionLShow.pack()
This way isn't recommended for your situation whatsoever, since that will involve mixing pack and grid together which could cause future errors in your code.
As Bryan Oakley said:
it will cause errors immediately, if the widgets share the same parent. It won't ever cause problems if the widgets have different parents.
You should instead do this:
from tkinter import *
from tkinter import ttk
class Application:
def __init__(self, master):#Change to game class when combining code
self.master = master#remove when combining code
self.frame_Canvas = ttk.Frame(self.master, width = 600, height = 600)
self.frame_Canvas.pack(side = 'left')
self.frame_Canvas.pack_propagate(False)
self.frame_Canvas.grid_propagate(False)
self.hangman = Canvas(self.frame_Canvas, width = 600, height = 600,
background = 'white').pack()
self.FullName = ttk.Label(self.frame_Canvas, text = "Full Name", background = 'white')#full name will be entered here
self.FullName.config(font=("TkDefaultFont", 20))
self.FullName.place(x = 10, y = 10)
self.frame_Interact = ttk.Frame(self.master, width = 200, height = 600)
self.frame_Interact.pack(side = 'right')
self.frame_Interact.pack_propagate(False)
self.frame_Interact.grid_propagate(False)
Grid.columnconfigure(self.frame_Interact, 0, weight=1) # NOTE, THIS CHANGED
self.QuestionLabel = ttk.Label(self.frame_Interact, text = "Question:")
self.QuestionLabel.config(font=("TkDefaultFont", 20))
self.QuestionLabel.grid(column = 0, row = 0)
self.QuestionShow = Text(self.frame_Interact, height=1, width=8)
self.QuestionShow.config(font=("TkDefaultFont", 20))
self.QuestionShow.grid(column = 0, row = 1)
def main():
root = Tk()
root.wm_title("Hangman")
Menu = Application(root)
root.resizable(width=False, height=False)
root.iconbitmap("windowicon.ico")
root.mainloop()
if __name__ == "__main__":
main()
By adding Grid.columnconfigure(self.frame_Interact, 0, weight=1) it will help grid know how to allocate extra space. so that the label will try to take up the whole column. The same goes for rows if you're wondering, you can add Grid.rowconfigure(self.frame_Interact, 0, weight=1) to make the widgets fill the whole row.
And for some improvements to your code, you should change this line:
self.hangman = Canvas(self.frame_Canvas, width = 600, height = 600,
background = 'white').pack()
# to
self.hangman = Canvas(self.frame_Canvas, width = 600, height = 600, background = 'white')
self.hangman.pack()
Or else self.hangman will be None, as it is in your code.

Resources