Tkinter continuously loop main event - python-3.x

I'm making a trivia game where data is from www.opentdb.com. I want it to continually loop through the main program after it tells you whether your answer is correct or not. (clicking an answer and shows green label but will stay at that screen). I would like it to wait a few seconds and have tried using .after(3000, main()) but do not know how to reset the screen and repeat.Im sorry if this sounds very confusing. Thank you for your assistance :)
from tkinter import *
import trivia
import html
t = trivia.Trivia()
root = Tk()
root.geometry("1366x768")
root.configure(bg='#4dc4ff')
root.iconbitmap("icon.ico")
root.title("Trivia Party!")
frame = Frame(root, bg='#4dc4ff')
class Buttons:
def __init__(self, text):
self.text = html.unescape(text) # remove html entities
self.correct_label = Label(root, text="Correct!", font=("Arial", 30), background="#98FB98", width=20, height=2) # green correct label
def reveal_answer(self):
if self.text == t.return_correct_answer(): # if its the right answer
self.correct_label.pack(pady=175)
frame.destroy()
else:
self.correct_label.config(text="Incorrect", background="#ff6347")
self.correct_label.pack(pady=175)
Label(root, background="#4dc4ff", text=("The correct answer was: " + t.return_correct_answer()),
font=("Arial", 40)).pack()
frame.destroy()
def create(self):
Button(frame, text=self.text, borderwidth=0, highlightbackground="#00abff", font=("Helvetica", 30),
command=self.reveal_answer).pack(side=LEFT, expand=YES)
frame.pack(fill=BOTH, expand=YES)
buttons = []
question = Label(frame, text=html.unescape(t.return_question()), font=("Arial", 25), height=3, bg="#00abff").pack(fill=X)
for i in t.all_answers: # t.all_answers is pulling all of the multiple choice answers ie "coke", "pepsi" "sprite"
buttons.append(Buttons(i).create()) # i is the individual answer eg "pepsi" and is making a button
root.mainloop()

Related

Make multiple tk.Toplevel windows embedded/unified in main tk window

So I'm trying to create a program which uses multiple tk.Toplevel windows. The problem with this is, that all windows show up seperated as their "own App", so when you alt tab, you switch between the toplevel windows.
The pseudocode would look something like this:
import tkinter as tk
top_levels = {}
def open_toplevel():
top_level = tk.Toplevel(root)
top_level.geometry("300x200+0+0")
top_levels.update({f"toplevel{len(top_levels.keys())}" : top_level})
root = tk.Tk()
button = tk.Button(root, command= open_toplevel)
button.place(x=0, y=0)
root.mainloop()
So my question, is: is there a way to unify them into "one window"?
If you want all of them to unify into one window then tk.Frame is a better widget to use instead of tk.Toplevel
The purpose of tk.Toplevel is to create a new temporary window, not an extra part of the window. But frames are a really good way to organise stuff.
This code below creates new frame every time you click the button. This is just a simple example. You can also use grid for widgets in a frame. I also put a border so you can see where the frames are located.
from tkinter import *
def open_frame():
frame = Frame(root, highlightbackground="black", highlightthickness=2)
lbl1 = Label(frame, text=f"Frame {len(frames) + 1} label 1")
lbl2 = Label(frame, text=f"Frame {len(frames) + 1} label 2")
lbl1.pack()
lbl2.pack()
frame.pack(padx=5, pady=5)
frames.append(frame)
root = Tk()
frames = []
btn = Button(root, text="Open Frame", command=open_frame)
btn.pack()
root.mainloop()
I hope this solution is helpful
EDIT
Use this code here to move the frames:
from tkinter import *
def open_frame():
global frame, frames
frame = Frame(root, highlightbackground="black", highlightthickness=2)
lbl1 = Label(frame, text=f"Frame {len(frames) + 1} label 1")
lbl2 = Label(frame, text=f"Frame {len(frames) + 1} label 2")
lbl1.pack()
lbl2.pack()
frame.pack(padx=5, pady=5)
frame_number = len(frames)
lbl1.bind('<B1-Motion>', lambda event: MoveWindow(event, frame_number))
lbl2.bind('<B1-Motion>', lambda event: MoveWindow(event, frame_number))
frame.bind('<B1-Motion>', lambda event: MoveWindow(event, frame_number))
frames.append(frame)
labels.append(lbl1)
labels.append(lbl2)
def MoveWindow(event, frame_number):
global root, frames
root.update_idletasks()
f = frames[frame_number]
x = f.winfo_width()/2
y = f.winfo_height()*1.5
f.place(x=event.x_root-x, y=event.y_root-y)
root = Tk()
root.geometry("500x500")
frames = []
labels = []
btn = Button(root, text="Open Frame", command=open_frame)
btn.pack()
root.mainloop()

What I am Attempting is if i click a radio button a number would be inserted into a text box. When button is clicked it prints to root

I have researched for 3 weeks and could find no solution.
I have read numerous tags and have also tried modifying code
Most of the tags refer to Java or some other programming language other than Python
My file has upwards of 80 frame which will all have 3 buttons and 1 textbox.
I sent over 1 frame with 3 radio buttons and a textbox.
Would this work or should I go in a different direction.
Any advice would be greatly appreciated.
from tkinter import *
from tkinter import Tk
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("Dental Milling Machines")
root.geometry("1000x900")
def func1(event):
insert("")
textbox1.insert('1.5')
def onclick1():
textbox1.insert('<Return>', func1)
button_var1 = tk.IntVar()
frame1 = Frame(root, height = 150, width= 150, relief= RAISED, bd=8, bg="blue")
frame1.grid(row=1, column=0, pady=2,sticky="NW")
frame2 = Frame(frame1, height = 150, width= 150, relief= RAISED, bd=8, bg="lightblue")
frame2.grid(row=1, column=0, pady=2,sticky="NW")
label = Label(frame2, text="Select # Of Units", fg="red")
label.grid(row=0, column=0, pady= 1, padx=3, sticky= "W")
textbox1 = Text(frame2, borderwidth=1, wrap="none", width=10, height=2)
textbox1.grid(row=0, column=1, sticky="w")
button1=Radiobutton(frame2, text="1 Unit ", variable=button_var1, command=onclick1)
button1.grid(row=1, column=0, pady= 1, padx= 5, sticky= "W")
root.mainloop()
If you want to insert something into the text box whenever the radio button is click, just modify your code as below:
def onclick1():
textbox1.delete('1.0', 'end') # clear the text box
textbox1.insert('end', '1.5') # insert whatever you want into text box
...
button1 = Radiobutton(frame2, text="1 Unit", variable=button_var1, command=onclick1)
...

Python 3 - Tkinter, MessageBox popsup when program is run not on button press [duplicate]

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 2 years ago.
I have a small issue for which I can`t find the reason.
I have the following GUI, and it pops the message box when I run it, even though it is inside a procedure which is triggered only on button press.
Tried even creating a secondary function which will show only the message box and still did not fix the issue.
Thank you for your help... I am quite sure that there is an easy fix which I just do not see...
import tkinter as tk
from tkinter import ttk
import tkinter.messagebox
import jl_generator
def run():
jl_generator.run_process()
tkinter.messagebox.showerror('Done','Done')
def show():
temp_list = user_input_list
for i in range(0, len(user_input_list[0])):
listBox.insert("", "end", values = (user_input_list[0][i],user_input_list[1][i],user_input_list[2][i],user_input_list[3][i],user_input_list[4][i],user_input_list[6][i],user_input_list[8][i]))
# Column Names for the TreeView
cols = ('Entity', 'Customer Nr', 'Account Code', 'Profit Centre', 'Partner Profit Centre', 'Amount', 'Nr Of Journal Lines')
# Input data for the tree view
user_input_list, journal_code = jl_generator.get_user_input()
#Creating the
root = tk.Tk()
root.title('JL Generator')
#Create the treeview
listBox = ttk.Treeview(root, columns=cols, show='headings')
for col in cols:
listBox.heading(col, text=col)
listBox.grid(row=1, column=0, columnspan=3)
#-------------LABELS--------------
#Title Label
label = tk.Label(root, text="Journal Lines Generator", font=("Arial",30)).grid(row=0, columnspan=3)
#Journal Code Label
show_journal_code = tk.Label(root, text = 'Journal Code = ' + journal_code).grid(row=6, column=1)
#Number of Journal Lines Label
show_number_of_journal_lines = tk.Label(root, text = 'Number of Journal Lines = ' + str(sum(user_input_list[8][i] for i in range(0, len(user_input_list[0]))))).grid(row=5, column=1)
#------------BUTTONS-----------
#Run the Generation
run_process = tk.Button(root, text="Generate JLs", width=15, command=run()).grid(row=4, column=1)
#Show the input data
showScores = tk.Button(root, text="Show Input", width=15, command=show).grid(row=4, column=0)
#Close the window
closeButton = tk.Button(root, text="Exit", width=15, command=exit).grid(row=4, column=2)
root.mainloop()
run_process = tk.Button(root, text="Generate JLs", width=15, command=run()).grid(row=4, column=1)
this is incorrect.
I used to feel confused about this.
You should use:
run_process = tk.Button(root, text="Generate JLs", width=15, command=run).grid(row=4, column=1)
In python,function is an object,call function should use function()
If you debug this code,you will find that after debug this code
run_process = tk.Button(root, text="Generate JLs", width=15, command=run()).grid(row=4, column=1)
you will find it will call run function and run it.
And Finally,run_process["command"] will be the returned value ofrun()

Adding padding query insert to Listbox

I have read a number of threads and other resources to try to find the correct way to handle this but I have not found anything that works with my application.
Here is what I am trying to accomplish.
When a query is completed and the insert of the data to a Listbox is done I cannot seem to get it to margin the data insert by 1 character space.
I am using pack() and I have read the tkinter manual for this and have tried each example available along with others found on various threads here.
The widget:
output = tkinter.Listbox(window_2, height = 20, font='Times 10',
width=42, bd=1, bg = '#FFD599', fg = '#9A0615', selectmode=SINGLE)
output.pack()
output.place(x=210, y=195)
I have tried padx and pady with pack() without success, although this works successfully with the Text widget. I have also attempted to use a few alternatives that I have found here on the site but all without success in margining the Listbox when the data is inserted.
Any advice?
pack's padx/pady and ipadx/ipady options don't affect the data that is inside the listbox. The listbox itself doesn't have any options to add an internal margin.
To get a margin around the inside of the listbox, what I normally do is give it a zero borderwidth and highlightthickness, and then place it in a frame with the same background color and let the frame be the border. You can then add any padding you want between the border and the listbox.
This is also convenient because you can put a scrollbar inside the frame, giving it the appearance that it is inside the listbox without actually being inside the listbox.
Example:
import tkinter as tk
root = tk.Tk()
root.configure(background="gray")
listbox_border = tk.Frame(root, bd=1, relief="sunken", background="white")
listbox_border.pack(padx=10, pady=10, fill=None, expand=False)
listbox = tk.Listbox(listbox_border, width=20, height=10,
borderwidth=0, highlightthickness=0,
background=listbox_border.cget("background"),
)
vsb = tk.Scrollbar(listbox_border, orient="vertical", command=listbox.yview)
listbox.configure(yscrollcommand=vsb)
vsb.pack(side="right", fill="y")
listbox.pack(padx=10, pady=10, fill="both", expand=True)
for i in range(100):
listbox.insert("end", "Item #{}".format(i))
root.mainloop()
here is a variation on the much appreciated answer by Bryan Oakley.
it uses ttk widgets instead of tk widgets
the scrollbar tracks your position in the list box when you scroll with the mouse
uses the oStyle.theme_use("clam") because it may look more modern...this can be commented out
'
import tkinter as tk
from tkinter import ttk
try: # allows the text to be more crisp on a high dpi display
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(1)
except:
pass
root = tk.Tk()
oStyle = ttk.Style()
oStyle.theme_use("clam")
oStyle.configure('LB.TFrame', bd=1, relief="sunken", background="white")
listbox_border = ttk.Frame(root, style='LB.TFrame')
listbox_border.pack(padx=4, pady=4, fill=None, expand=False)
vsb = ttk.Scrollbar(listbox_border)
vsb.pack(side="right", fill="y")
listbox = tk.Listbox(listbox_border, width=20, height=10, borderwidth=0,
highlightthickness=0, selectmode=tk.SINGLE,
activestyle=tk.NONE)
listbox.pack(padx=6, pady=6, fill="y", expand=True)
listbox.config(yscrollcommand=vsb.set)
vsb.config(command=listbox.yview)
for i in range(100):
listbox.insert("end", "Item #{}".format(i))
root.mainloop()
'
first of all to format chars in a tkinter listbox you need to use a fixed font and .format python funcion....;
So you can do something this
Press Load to load data in the listbox and pay attention to this line code
s = '{0:>8}{1:5}'.format(i[0],i[1])
self.list.insert(tk.END, s)
import tkinter as tk
RS = (('Apple',10),
('Banana',20),
('Peack',8),
('Lemon',6),)
class App(tk.Frame):
def __init__(self,):
super().__init__()
self.master.title("Hello World")
self.init_ui()
def init_ui(self):
self.pack(fill=tk.BOTH, expand=1,)
f = tk.Frame()
sb = tk.Scrollbar(f,orient=tk.VERTICAL)
self.list = tk.Listbox(f,
relief=tk.GROOVE,
selectmode=tk.BROWSE,
exportselection=0,
background = 'white',
font='TkFixedFont',
yscrollcommand=sb.set,)
sb.config(command=self.list.yview)
self.list.pack(side=tk.LEFT,fill=tk.BOTH, expand =1)
sb.pack(fill=tk.Y, expand=1)
w = tk.Frame()
tk.Button(w, text="Load", command=self.on_callback).pack()
tk.Button(w, text="Close", command=self.on_close).pack()
f.pack(side=tk.LEFT, fill=tk.BOTH, expand=0)
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=0)
def on_callback(self,):
for i in RS:
s = '{0:>8}{1:5}'.format(i[0],i[1])
self.list.insert(tk.END, s)
def on_close(self):
self.master.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()

How to create a sub frames with a specific layout?

I'm aiming to make a login program but the only part that confuses me is how to make the frames.I need 3 different frames but I neither know how to make a frame other the then like this:
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)
and I can only make labels and widgets using that single mainframe. As far as making another one, it is beyond me. I need to know exactly place widets inside of each frame and even after creating frames I don't know how to place stuff on the grid. Would I go for the overall grid, or does something change after making the grid. I'm using the following layout for making the frame. Basically i'm hoping for a crash course in frames. Any information i've gathered doesn't make sense to me, even after I tried to put it into code.
I've got the coding part down just not the frame part.
#Import tkinter to make gui
from tkinter import *
from tkinter import ttk
import codecs
def login(*args
):
file = open("rot13.txt", "r")
lines = file.readlines()
uname = user.get()
pword = pw.get()
for i in lines:
x = i.split()
if codecs.encode(uname,'rot13') == x[0] and codecs.encode(pword,'rot13') == x[1]:
result.set("Successful")
break;
else:
result.set("Access Denied")
root = Tk()
root.title("Login")
#Configures column and row settings and sets padding
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)
user = StringVar()
pw = StringVar()
result = StringVar()
user_entry = ttk.Entry(mainframe, width=20, textvariable=user)
user_entry.grid(column=2, row=1, sticky=(W, E))
pw_entry = ttk.Entry(mainframe, width=20, textvariable=pw)
pw_entry.grid(column=2, row=2, sticky=(W, E))
ttk.Label(mainframe, text="Username ").grid(column=1, row=1, sticky=W)
ttk.Label(mainframe, text="Password ").grid(column=1, row=2, sticky=W)
ttk.Label(mainframe, text="").grid(column=1, row=3, sticky=W)
ttk.Label(mainframe, text="Result").grid(column=1, row=4, sticky=W)
ttk.Label(mainframe, text="").grid(column=1, row=5, sticky=W)
ttk.Button(mainframe, text="Login", command=login).grid(column=3, row=6, sticky=W)
#Makes a spot to put in result
ttk.Label(mainframe, textvariable=result).grid(column=2, row=4, sticky=(W, E))
#Opens up with item selected and allows you to enter username without having to click it
user_entry.focus()
#Runs calculate if click enter
root.bind('<Return>', login)
root.mainloop()
I believe the key point that you are missing is that subframes of mainframe use mainframe as the parent and that widgets within subframes use the subframe as parent. Furthermore, you can then place the subframe within the mainframe and the subframe widgets within the subframe. You do not have to pass parents to .grid because each widget knows its parent. A simplified example:
from tkinter import *
root = Tk()
mainframe = Frame(root)
login = Frame(mainframe)
label = Label(login, text='label')
entry = Entry(login)
display = Frame(mainframe)
result = Label(display, text='display result')
mainframe.grid() # within root
login.grid(row=0, column=0) # within mainframe
label.grid(row=0, column=0) # within login
entry.grid(row=0, column=1) # within login
display.grid() # within mainfram
result.grid(row=2, column=0) # within display

Resources