How do you use tkinter entry boxes to make a variable? - python

I'm currently trying to create an "orbit simulator" and this part of the code is part of it. However an errors occur when I try to run it. The get() function seems to not work as it simply outputs that it doesn't exist.
I'm really stumped at this.
import tkinter
runwin = tkinter.Tk()
runwin.title("Orbit Sim")
runwin.geometry("320x320")
def run21():
dt=ent21.get("1.0")
tg=ent22.get("1.0")
xz=ent23.get("1.0")
yz=ent24.get("1.0")
velz=ent25.get("1.0")
runwin.destroy()
lbl21 = tkinter.Label(runwin, text="How long to simulate for?").pack()
ent21 = tkinter.Entry(runwin).pack()
lbl22 = tkinter.Label(runwin, text="How many seconds pass per check?").pack()
ent22 = tkinter.Entry(runwin).pack()
lbl23 = tkinter.Label(runwin, text="Starting Positon? Please state X then Y.").pack()
ent23 = tkinter.Entry(runwin).pack()
ent24 = tkinter.Entry(runwin).pack()
lbl24 = tkinter.Label(runwin, text="Starting Velocity").pack()
ent25 = tkinter.Entry(runwin).pack()
btn21 = tkinter.Button(runwin, text="Submit", command=run21).pack()
runwin.mainloop()
t=0
while t < dt:
r3, t =m.sqrt((xz*xz)+(yz*yz)), t+tg
P.S. I'm not a genius at coding and the way i've written this code is pretty much the only way I can understand it without sufficient notes.

You have 3 problems I can see.
1st problem is you are using the grid manager on the widget directly and this will cause your get() method to error out. That is be cause the grid manager is returning None.
We can fix this by calling the grid manager on a new line.
2nd you are putting "1.0" in the get method and this is going to error out.
Just leave it blank like this get().
3rd you need to define your variables to be ran after the program closes as outside of the tkinter instance. Then you need to set the global call up in your function.
Take a look at the below code:
import tkinter
# the 5 below variables are listed outside of tkinter so your while statement
# after the mainloop can use the data.
dt = ""
tg = ""
xz = ""
yz = ""
velz = ""
runwin = tkinter.Tk()
runwin.title("Orbit Sim")
runwin.geometry("320x320")
def run21():
# this global is needed to tell the run21 function the variables are
# in the global namespace.
global dt, tg, xz, yz, velz
dt=ent21.get()
tg=ent22.get()
xz=ent23.get()
yz=ent24.get()
velz=ent25.get()
runwin.destroy()
lbl21 = tkinter.Label(runwin, text="How long to simulate for?").pack()
ent21 = tkinter.Entry(runwin)
ent21.pack()
lbl22 = tkinter.Label(runwin, text="How many seconds pass per check?").pack()
ent22 = tkinter.Entry(runwin)
ent22.pack()
lbl23 = tkinter.Label(runwin, text="Starting Positon? Please state X then Y.").pack()
ent23 = tkinter.Entry(runwin)
ent23.pack()
ent24 = tkinter.Entry(runwin)
ent24.pack()
lbl24 = tkinter.Label(runwin, text="Starting Velocity").pack()
ent25 = tkinter.Entry(runwin)
ent25.pack()
btn21 = tkinter.Button(runwin, text="Submit", command=run21).pack()
t=0
runwin.mainloop()
print(dt, tg, xz, yz, velz)
# commented out as it is not testable without knowing what "m" is.
# while t < dt:
# r3, t = m.sqrt((xz*xz)+(yz*yz)), t+tg

Don't define a widget and use the layout manager on the same line if you wish to use the Widget.
i.e DON'T DO THIS
ent21 = tkinter.Entry(runwin).pack()
DO THIS
ent21 = tkinter.Entry(runwin)
ent21.pack()
Like so
import tkinter
runwin = tkinter.Tk()
runwin.title("Orbit Sim")
runwin.geometry("320x320")
dt = ""
tg = ""
xz = ""
yz = ""
velz = ""
def run21():
global dt, tg, xz, yz, velz
dt=ent21.get()
tg=ent22.get()
xz=ent23.get()
yz=ent24.get()
velz=ent25.get()
runwin.destroy()
lbl21 = tkinter.Label(runwin, text="How long to simulate for?").pack()
ent21 = tkinter.Entry(runwin)
ent21.pack()
lbl22 = tkinter.Label(runwin, text="How many seconds pass per check?").pack()
ent22 = tkinter.Entry(runwin)
ent22.pack()
lbl23 = tkinter.Label(runwin, text="Starting Positon? Please state X then Y.").pack()
ent23 = tkinter.Entry(runwin)
ent23.pack()
ent24 = tkinter.Entry(runwin)
ent24.pack()
lbl24 = tkinter.Label(runwin, text="Starting Velocity").pack()
ent25 = tkinter.Entry(runwin)
ent25.pack()
btn21 = tkinter.Button(runwin, text="Submit", command=run21).pack()
runwin.mainloop()
One of the other commenters is correct too. Any code after .mainloop will not run until after the window is closed. Consider doing this inside the run21 function so it happens when the button is pressed.
I've removed "1.0" from your get since the get method of an entry widget doesn't take any arguments.
Also please consider better naming for your variables. Instead of ent21 consider entrySimulationTime or instead of ent24 consider entryStartingPosY. Your code will be much easier to understand that way.
EDIT: Added globals to expand the scope of dt etc.

Related

Button command doesn't updates my Label textvariable

I have this very easy program which I want to display one random line from a file each time I click on the Button.
Problem is a new line is display at startup of the program, but nothing happens when I click the button, can someone explain me why ?
from random import randrange
from tkinter import *
def entree():
n=randrange(251)
fs = open('lexique','r')
liste = fs.readlines()
return liste[n]
fen = Tk()
fen.title("lexique anglais politique")
defi = StringVar()
defi.set(entree())
lab = Label(fen, textvariable=defi).pack()
Button(fen, text='Beste Bat', command=entree).pack()
fen.mainloop()
As stated in one of the comments (by #matszwecja), your entree() function doesn't really do anything appart from returning a value.
Nothing in your code updates the actual label. Try something like this :
from random import randrange
from tkinter import *
def entree():
n=randrange(251)
fs = open('lexique','r')
liste = fs.readlines()
return liste[n]
def update_label():
lab.config(text=entree())
fen = Tk()
fen.title("lexique anglais politique")
lab = Label(fen, text=entree())
lab.pack()
Button(fen, text='Beste Bat', command=update_label).pack()
fen.mainloop()
In this example, the entree() function is used to go get a line from your file, and the update_label() function is used to actually update the label.
Also, if you want to be able to update a label, you'll have to pack it after assigning it to a variable.
On a side note, it could be worth noting that hardcoding values that could change in the future is generally considered bad practice. In that regard, I think coding the entree() function this way might be a better idea :
def entree():
fs = open('lexique','r')
liste = fs.readlines()
n=randrange(len(liste))
return liste[n]
This way, if you ever add or remove lines to your "lexique" file, you will not have to change the code.

How to make a Tkinter loading screen that runs side-by-side the main program

I am trying to create a small python app with Tkinter which involves collecting data in several lists, like this:
def main_process():
list1 = []
list2 = []
list3 = []
#Data collection process
#Resulting lists ready
The data collection process takes about 10 seconds, so I wanted to make a "Loading Screen" that works side-by-side the data collection process, without one following the other. For the status bar, I thought of introducing a variable n with value starting from 0, and increasing its value as and when each resultant lists get ready.
I attempted to create a function loading_screen(n) which will be called before the data processing, with n being the aforementioned variable containing a numeric value. And as the data_processing occurs, I shall run the loading_screen function to introduce the updated values of n, like so:
def main_process():
def loading_screen(n):
root = Tk()
root.title('Stock Market App')
root.geometry('800x300')
status_label = Label(root, text = n)
status_label.pack()
loading_screen(0)
# list1 Ready
n += 10
root.after(0, lambda: loading_screen(n))
# list2 ready
n += 10
root.after(0, lambda: loading_screen(n))
# And so on...
But it ended up showing me the loading screen after all the data processing has occurred. Can somebody please help me on this?
Thanks.
Putting function inside a function is not a good idea, better to create a class.
Then create two separate Tk windows.
Also, after is able to pass parameters so lambda is not necessary.
I've made a few small adjustments to conform to your question. The windows will now sit side by side.
Is this what you were looking for?
import tkinter as tk
class main_process:
def loading_screen( self, n ):
self.status_label[ 'text' ] = '{}'.format( n )
self.root.deiconify()
self.status_label.focus()
self.root.update()
# insert processing here
def __init__( self, x ):
self.root = tk.Tk()
self.root.withdraw()
self.root.title('Stock Market App')
self.root.geometry( f'700x300+{x}+240')
self.status_label = tk.Label( self.root, font = 'Helvetica 20 normal')
self.status_label.pack()
self.root.update()
screen_1 = main_process( 0 )
screen_1.root.after( 10, screen_1.loading_screen, 1 )
screen_2 = main_process( 705 )
screen_2.root.after( 10, screen_2.loading_screen, 2 )
screen_2.root.mainloop()
Hi you can remove the gui like this
Labelone.destroy()
.
.
.
Loading_screen.place(...)
#you data collection code
Loading_screen.destroy()
Labelone.place(...)
.
.
.
So your data collection is going background and loading gui is place so user sees loading while python is collecting data

The radiobuttons cannot be unselected by default

def booking(self):
self.book_win = tk.Tk()
self.book_win.geometry('1000x600')
self.fm7 = tk.Frame(self.book_win,width=300,height=500)
self.fm7.grid(row=0,column=0,rowspan=3,columnspan=2)
self.fm8 = tk.Frame(self.book_win,width=300,height=500)
self.fm8.grid(row=0,column=2,rowspan=3,columnspan=2)
self.book_title = tk.Label(self.fm7,text='Room Prices per night',font=('Helvetica',24))
self.book_title.grid(row=0,columnspan=2,padx=10,pady=10)
self.lb_deluxe = tk.Label(self.fm7,text='Deluxe Room',font=('Helvetica',20))
self.lb_deluxe.grid(row=1,column=0,padx=10,pady=10)
self.pr1 = tk.Label(self.fm7,text=2050,font=('Helvetica',20))
self.pr1.grid(row=1,column=1,padx=10,pady=10)
self.lb_twin = tk.Label(self.fm7,text='Twin Bedroom',font=('Helvetica',20))
self.lb_twin.grid(row=2,column=0,padx=10,pady=10)
self.pr2 = tk.Label(self.fm7,text=2500,font=('Helvetica',20))
self.pr2.grid(row=2,column=1,padx=10,pady=10)
self.lb_king = tk.Label(self.fm7,text='King Bedroom',font=('Helvetica',20))
self.lb_king.grid(row=3,column=0,padx=10,pady=10)
self.pr3 = tk.Label(self.fm7,text=2890,font=('Helvetica',20))
self.pr3.grid(row=3,column=1,padx=10,pady=10)
self.lb_harbour = tk.Label(self.fm7,text='Harbour View Room',font=('Helvetica',20))
self.lb_harbour.grid(row=4,column=0,padx=10,pady=10)
self.pr4 = tk.Label(self.fm7,text=3120,font=('Helvetica',20))
self.pr4.grid(row=4,column=1,padx=10,pady=10)
self.var1 = tk.BooleanVar()
self.ck_breakfast = tk.Checkbutton(self.fm8,text='Breakfast',variable=self.var1,font=('Helvetica',20))
self.ck_breakfast.grid(row=0,columnspan=2,padx=10,pady=10)
self.lb_room = tk.Label(self.fm7,text='Type of Room',font=('Helvetica',20))
self.lb_room.grid(row=6,columnspan=2,padx=10,pady=10)
self.var2 = tk.StringVar()
self.var2.set(' ')
self.rb1 = tk.Radiobutton(self.fm7,text='Deluxe Room',variable=self.var2,value='Deluxe Room')
self.rb1.grid(row=7,column=0,padx=10,pady=10)
self.rb2 = tk.Radiobutton(self.fm7,text='Twin Bedroom',variable=self.var2,value='Twin Bedroom')
self.rb2.grid(row=7,column=1,padx=10,pady=10)
self.rb3 = tk.Radiobutton(self.fm7,text='King Bedroom',variable=self.var2,value='King Bedroom')
self.rb3.grid(row=8,column=0,padx=10,pady=10)
self.rb4 = tk.Radiobutton(self.fm7,text='Harbour View Room',variable=self.var2,value='Harbour View Room')
self.rb4.grid(row=8,column=1,padx=10,pady=10)
In this program, I would like to set all the radiobuttons at the beginning to be unselected. Although I tried self.var2.set(' ') it still does not work. I have already tried to find the solution and change thoroughly but there may have some problems occur. If possible can anyone help me to find out the problem?
The problem with your code was that it has more than one instance of Tk() hence the StringVar() doesnt know which instance to belong to(by default the first created Tk()). So make all additional child windows to Toplevel() and just keep one Tk() for the entire code, unless you exactly know what your doing.
Or if you still wish to keep multiple Tk() then give a master to almost all the extra widgets or variables created(StringVar,IntVar,PhotoImage,etc) like:
self.var2 = tk.StringVar(master=self.book_win)
This is might not work
self.var2 = tk.StringVar()
self.var2.set(None) #set this to none
self.rb1 = tk.Radiobutton(self.fm7,text='Deluxe Room',variable=self.var2,value='Deluxe Room')
self.rb1.grid(row=7,column=0,padx=10,pady=10)
self.rb2 = tk.Radiobutton(self.fm7,text='Twin Bedroom',variable=self.var2,value='Twin Bedroom')
self.rb2.grid(row=7,column=1,padx=10,pady=10)
self.rb3 = tk.Radiobutton(self.fm7,text='King Bedroom',variable=self.var2,value='King Bedroom')
self.rb3.grid(row=8,column=0,padx=10,pady=10)
self.rb4 = tk.Radiobutton(self.fm7,text='Harbour View Room',variable=self.var2,value='Harbour View Room')
self.rb4.grid(row=8,column=1,padx=10,pady=10)

how to generate multiple buttons with a loop?

I have programmed software that displays a "tuile".
Definition of a tuile:
A tuile is a Frame which contains a button which displays an image and an explanatory text.
I would like to display 3 tuiles with 3 different settings.
listes_icones = ["icone1.png","icone2.png","icone3.png"]
listes_relx = [".3",".4",".5"]
listes_text = ["SYSTEM", "USER", "GAME"]
for i in range(3):
gen_img = PhotoImage(file=listes_icones[i])
gen_cadre = Frame(home,width=100, height=100,bg=bg_root)
gen_cadre.place(anchor="c", relx=listes_relx[i], rely=.5)
gen_img_bouton = Button(gen_cadre, image=gen_img, relief="flat",bg=bg_root)
gen_img_bouton.pack()
gen_text = Label(gen_cadre, text=listes_text[i], bg=bg_root, fg=text_color,font="blocktastic 18")
gen_text.pack()
I manage to display the text but not the button and the image, the variable is overwritten. How to solve this problem?
The problem that you are facing is like you said, the variable is overwritten in your loop. To solve this you need to keep track of your generated images. A simple solution is to store them in a list and get them in the next step. Here is an exampel:
import tkinter as tk
import PIL
listes_icones = ["icone1.png","icone2.png","icone3.png"]
gen_icons = []
listes_relx = [".3",".4",".5"]
listes_text = ["SYSTEM", "USER", "GAME"]
home = tk.Tk()
for i in range(3):
gen_img = tk.PhotoImage(file=listes_icones[i])
gen_icons.append(gen_img)
gen_cadre = tk.Frame(home,width=100, height=100)
gen_cadre.place(anchor="c", relx=listes_relx[i], rely=.5)
gen_img_bouton = tk.Button(gen_cadre, image=gen_icons[i], relief="flat")
gen_img_bouton.pack()
gen_text = tk.Label(gen_cadre, text=listes_text[i], font="blocktastic 18")
gen_text.pack()
home.mainloop()

Get random text variable from tkinter button - python

The title might be a little confusing, so i will describe my question more.
I making a little program that will assist me with studying Chinese, just for myself. This will aid me with coding and in same time with studying.
I encounter a problem with getting the text variable from my button, without function the code work like wanted. But when trying to get random text that shown on the button it cause me a problem, because text doesn't come. All i need it, when button is pressed function check if input is the same as character and give correct/incorrect notice.
I little new to coding, so it can be simple matter, but still appreciate any help.
The code:
#========== Imports ===========#
from tkinter import *
from tkinter import messagebox
import random
#========== Parameters ==========#
CN = Tk()
CN.title("Chinese Test")
CNW = ["爱","八","爸爸","杯子","北京","本","不客气","不","菜","茶","吃","出租车","打电话",
"大","的","点","电脑","电视","电影","东西","都","读","对不起","多","多少","儿子",
"二","饭店","飞机","分钟","高兴","个","工作","汉语","好","号","喝","和","很","后面","回","会","几","家","叫","今天"]
Cword = ""
Cent = StringVar()
def butPress():
global Cword
if (B0.text==Cword): #wrong way to get text
messageText = "Correct"
else:
messageText = "Incorrect"
CNEntry = Entry(CN,textvariable = Cent).grid(row = 0, column = 1, columnspan = 8)
B0 = Button(CN, text = random.choice(CNW),command = lambda:butPress,bd = 3, width = 5, height = 3).grid(row = 6, column = 4, padx = 10, pady = 10)
#========== Pack ==========#
CN.mainloop( )
There's a few things.
First of all, command = lambda:butPress doesn't work. Use command = butPress. You should only use a lambda when you need to pass parameters (e.g. command = lambda:butPress(parameter)), which you don't.
Then there's B0.text. Because you do
B0 = Button(...).grid(...)
B0 is None, because that is what grid() returns. Change it to
B0 = Button(...)
B0.grid(...)
This way B0 is a Button object. To get the current text of it you can't use B0.text, you have to use B0['text'].
You then compare the text to Cword, which is '' and never changes. If you want to compare it to the entered text in the Entry use CNEntry.get() (after again putting grid on a separate line).

Categories