Python GUI - entry values are not strings but methods? - python

I have a small problem with my program :
I can't to understand how to reuse the entered values in the program. It says that I cannot use float on a method, but I thought that the s.get gave me a string. It also seems that it doesn't actually use the values entered but just the default values.
Does anyone know what is wrong with the code?
from tkinter import *
from math import *
import numpy as np
import matplotlib.pyplot as plt
class GUI:
def __init__(self):
self.root = Tk()
self.labelVariable = StringVar()
self.root.title('Projet informatique')
self.initialize()
self.root.mainloop()
def initialize(self):
self.main = Frame(self.root)
self.main.pack()
label = Label(self.main, textvariable=self.labelVariable, font=('courier',10,'bold'), anchor="w", fg="red", bg="white")
label.pack()
self.labelVariable.set(u"Modélisation de populations atteintes d'un virus")
v=Listbox(self.main)
v.insert("end","Modèle SIR")
v.insert("end", "Modèle de Witowski")
v.insert("end", "Modèle de Munz")
v.insert("end", "Modèle avec traitement")
v.bind("<Double-Button-1>", self.Double)
v.pack(expand=1,fill=BOTH)
def Double(self,event):
widget = event.widget
selection = widget.curselection()
value = widget.get(selection[0])
self.newWindow(value)
def ModifyTextarea(self,elem,msg,clear=None):
elem.config(state=NORMAL)
if clear:
elem.delete(1.0, END)
else:
elem.insert(END,msg)
elem.config(state=DISABLED)
def newWindow(self,msg):
top = Toplevel(self.root)
q1 = Frame(top)
q1.pack()
top.grab_set()
text = Text(q1,state=DISABLED,exportselection=True)
text.pack()
lbl = Label(q1,text="")
lbl.pack()
self.ModifyTextarea(text,msg)
v2=StringVar()
v3=StringVar()
v4=StringVar()
v5=StringVar()
v1=StringVar()
v6=StringVar()
e1=Entry(q1,textvariable=v1)
e2=Entry(q1,textvariable=v2)
e3=Entry(q1,textvariable=v3)
e4=Entry(q1,textvariable=v4)
e5=Entry(q1,textvariable=v5)
e6=Entry(q1,textvariable=v6)
e1.pack()
e2.pack()
e3.pack()
e4.pack()
e5.pack()
e6.pack()
v1.set("Taux de mortalité des zombies")
v2.set("Coefficient de propagation du virus")
v3.set("Taux de rémission")
v4.set("Taux d'infectés devenant malades")
v5.set("Taux de guérison")
v6.set("Pas de temps")
a=v1.get
b=v2.get
ze=v3.get
T=v4.get
N=v5.get
dt=v6.get
def callback():
print (a,b,ze,T,N,dt)
btnquit = Button(q1,width = 1,text = "Ok",command =callback())
btnquit.pack()
def zombies(a,b,ze,T,N,dt) :
a=flot(a)
b=float(b)
ze=float(ze)
T=float(T)
N=float(N)
dt=float(dt)
n = T/dt
n=int(n)
t=np.zeros((n+1))
for i in range (0,n):
t[0]=0
t[i+1]=t[i]+dt
s = np.zeros((n+1))
z = np.zeros((n+1))
r = np.zeros((n+1))
s[0] = N-1
z[0] =1
r[0] = 0
for i in range (n-1):
s[i+1] = s[i] + dt*(-b*s[i]*z[i])
z[i+1] = z[i] + dt*(b*s[i]*z[i]-a*s[i]*z[i]+ze*r[i])
r[i+1] = r[i] + dt*(a*s[i]*z[i]- ze*r[i])
if s[i+1]<0 or s[i+1] >N:
s[i+1]=0
break
if z[i+1] > N or z[i+1] < 0:
z[i+1]=0
break
if r[i+1] <0 or r[i+1] >N:
r[i+1]=0
break
return (t,s,r,z,i+2)
t,s,r,z,i=zombies(a,b,ze,T,N,dt)
plt.plot(t[:i],s[:i],'b')
plt.plot(t[:i],z[:i],'r')
plt.plot(t[:i],r[:i],'y')
plt.show()
if __name__ == "__main__":
app = GUI()

These are all incorrect:
a=v1.get
b=v2.get
ze=v3.get
T=v4.get
N=v5.get
dt=v6.get
You aren't calling the get function. To call it, add parenthesis:
a=v1.get()
...
FWIW, you don't need to use StringVars for each entry widget. You can just as easily get the value directly from the widget. Using StringVar often just adds extra code and extra objects to manage with no real benefit.

The problem is that you are just referencing the get methods, but not calling them. Also, you should make sure to call those functions inside your callback function, otherwise you will always print the values those variables had when you initialized your UI. Finally, note that instead of binding the callback to the button, you are executing the function once and binding its result to the button.
def callback():
# here, you have to ADD () to get the result of get
print(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get())
# here, you have to REMOVE () to get the callback function itself
btnquit = Button(q1, width=1, text="Ok", command=callback)
Similarly in your zombies function:
def zombies(): # no need for input parameters
a = float(v1.get())
b = float(v2.get())
etc.
And finally, you have to bind that function to a button (maybe instead of that callback function?), otherwise zombies will be executed just once at the end of the newWindow method, i.e. before inputting any actual data. The plotting should probably also be part of that function.

Related

Multiple listboxs bound to same function

Honestly, I do not feel like this should be happening, but it is.
self.marketList.bind('<<ListboxSelect>>', self.market_selected)
self.jobsList.bind('<<ListboxSelect>>', self.job_selected)
There really isn't any more interaction between these two functions. When you click on an item in the marketList box, it's supposed to bring up the jobs in the jobsList box. Currently, it is applying the binding two both boxes. When I click on a job entry in the jobsBox, it clears the jobs and my troubleshooting is showing that it's calling market_selected. I'm not sure why this is happening, but it's really messing with what I'm trying to do with it.
How can I ensure that my binding is on only one widget, and won't be applied to multiple widgets?
edit:
I'm told that this isn't enough code to reproduce the error.
This is all the relevant code.
As I said previously, self.market_selected is called when I click on anything in the jobsList
edit #2
I uploaded the entire script.
import MarketWizard
import JobWizard
import SpanWalkerDocuments as swd
import tkinter as tk
from tkinter import *
from tkinter import ttk
import SpanWalkerDocuments as SpanWalker
class SpanWalker:
def __init__(self):
self.root = tk.Tk()
self.root.title('Luke Spanwalker')
self.root.resizable(True, True)
self.MainFrame = Frame(self.root, bg='red')
self.sidebarFrame = Frame(self.root, bg='blue')
self.tabControl = ttk.Notebook(self.sidebarFrame)
self.marketFrame = Frame(self.tabControl)
self.clientFrame = Frame(self.tabControl)
self.jobsFrame = Frame(self.tabControl)
self.polesFrame = Frame(self.MainFrame, height=100, width=50)
self.tabControl.add(self.marketFrame, text="Markets")
self.tabControl.add(self.jobsFrame, text="Jobs")
self.tabControl.add(self.clientFrame, text="Clients")
#self.tabControl.add(self.polesFrame, text="Poles")
self.tabControl.pack(expand=1, fill="both")
self.MainFrame.grid(row=0, column=1)
self.sidebarFrame.grid(row=0, column=0)
#Awesome, our tabbed control is ready.
#Now, we need to make the listBox widgets that will actually display our data.
self.marketList = Listbox(self.marketFrame)
self.jobsList = Listbox(self.jobsFrame)
self.polesList = Listbox(self.polesFrame)
#Binding functions! Yay for binding functions!
self.marketList.bind('<<ListboxSelect>>', self.market_selected)
self.jobsList.bind('<<ListboxSelect>>', self.job_selected)
self.curMarket = ""
self.markets = []
self.jobs = []
self.poles = []
def UpdateMarkets (self):
if len(self.markets) > 0:
self.markets.clear()
self.marketList.delete(0, END)
for mark in swd.Market.objects:
self.markets.append(mark)
for m in range(0, len(self.markets)):
self.marketList.insert(m, self.markets[m].title)
def OpenMarketWizard(self):
mw = MarketWizard.MarketWizard()
mw.RunMarketWizard(self.root)
def market_selected(self, event):
print("Market Selected")
selection = self.marketList.curselection()
selectedMarket = ",".join([self.marketList.get(i) for i in selection])
self.PopulateJobs(selectedMarket)
def PopulateJobs(self, market):
self.jobs.clear()
self.jobsList.delete(0, END)
self.GetJobs(market)
for j in range(0, len(self.jobs)):
self.jobsList.insert(j, self.jobs[j].jobName)
def GetJobs(self, market):
marketJobs = []
jobs = []
if market =="":
return
for j in swd.Job.objects:
jobs.append(j)
print("Market = {0}".format(market))
for i in jobs:
if i.market == market:
self.jobs.append(i)
def job_selected(self, event):
print("Job Selected")
selection = self.jobsList.curselection()
selectedJob = ",".join([self.jobsList.get(i) for i in selection])
print("The selected job is: {0}".format(selectedJob))
def PopulateMarkets(self):
self.marketList.destroy()
for m in range(0, len(self.markets)):
marketList.insert(m, self.markets[m].title)
def OpenJobWizard(self):
jw = JobWizard.JobWizard()
jw.RunJobWizard(self.root)
#We need to pack everything that belongs in our tabbed function.
def marketListDisplay(self, show):
if show == True:
self.marketList.pack(fill="both")
self.UpdateMarkets()
self.newButton=Button(self.marketFrame, text="Open Market Wizard", command=lambda:self.OpenMarketWizard())
self.newButton.pack()
self.refreshButton=Button(self.marketFrame, text="Refresh Markets", command=lambda:self.UpdateMarkets())
self.refreshButton.pack()
else:
self.marketList.forget_pack()
def jobsListDisplay(self, show):
if show==True:
self.jobsList.pack()
self.newButton = Button(self.jobsFrame, text="Create New Job / Open Job Wizard", command=lambda:self.OpenJobWizard())
self.newButton.pack()
else:
self.jobsList.forget_pack()
def polesListDisplay(self, show):
if show==True:
self.polesList.pack()
else:
self.polesList.forget_pack()
#Query, why not put them all in a function?
sp = SpanWalker()
sp.marketListDisplay(True)
sp.polesListDisplay(True)
sp.jobsListDisplay(True)
The answer was given by acw1668. I just added exportselection=0 during the creation process for the listboxes, and it worked perfectly.
Sorry for the confusion, y'all, I've never really asked for help on here before.
Thanks for the help!

Python Tkinker, checkbox append string value to list?

The following code allows me to add/remove France/Italy from a list when clicked on a tkinter checkbox. Rather than passing 0 or 1 to the Add_France function, is it possible to pass through a string "France"? Thanks
My_list = []
root = Tk()
Country_Variable1 = tkinter.IntVar()
Country_Variable2 = tkinter.IntVar()
def Add_France():
if Country_Variable1.get() == 1:
My_list.append("France")
if Country_Variable1.get() == 0:
My_list.remove("France")
print (My_list)
def Add_Italy():
if Country_Variable2.get() == 1:
My_list.append("Italy")
if Country_Variable2.get() == 0:
My_list.remove("Italy")
print (My_list)
check1 = Checkbutton(root, text='France',variable=Country_Variable1, onvalue=1, offvalue=0, command=Add_France)
check2 = Checkbutton(root, text='Italy',variable=Country_Variable2, onvalue=1, offvalue=0, command=Add_Italy)
check1.pack()
check2.pack()
root.mainloop()
You have control over onvalue and offvalue with Checkbutton, so you can play around with the value that it returns in each state. In the below example, you can see how to make checkbutton based on inputs inside the list(assuming it is a list of strings of characters always):
from tkinter import *
root = Tk()
def process(var,text):
try:
val = int(var.get()) # If not selected it will give 0 as int, which will trigger `else` block
except ValueError:
val = var.get()
if val: # if val is not empty, ie, if val is any selected value
slct_ctry_lst.append(val)
else: # if val is 0
slct_ctry_lst.remove(text) # Remove the corresponding text from the list
print(slct_ctry_lst)
slct_ctry_lst = []
countries = ['France','Italy','China','Russia','India']
for idx,i in enumerate(countries):
var = StringVar(value=" ")
Checkbutton(root,text=i,variable=var,command=lambda i=i,var=var: process(var,i),onvalue=i).grid(row=0,column=idx)
root.mainloop()
Thought it would be easier, but maybe not suitable, with ttk.Combobox:
def process(e):
val = e.widget.get()
if val in slct_ctry_lst: # If val present inside the list
slct_ctry_lst.remove(val) # Remove it
else:
slct_ctry_lst.append(val)
print(slct_ctry_lst)
slct_ctry_lst = []
countries = ['France','Italy','China','Russia','India']
cbox = ttk.Combobox(root,values=countries,state='readonly')
cbox.pack(padx=10,pady=10)
cbox.set('Select a country')
cbox.bind('<<ComboboxSelected>>',process)
If the objective here is to make the code reusable for multiple countries, here is initial idea, this can be improved further.
Basically, I am using lambda expression here to the callback command function and passing the country name.
Created a separate function to create a checkbox which is generic for all the countries.
Now to add a new country to the list all you need to do is add an element to the available_country_list
You can make available_country_list global or can pass that as an argument to the list_countries function.
import tkinter
from tkinter import Checkbutton
selected_country_list = []
def select_country(country_name, variable):
global selected_country_list
print(variable)
if variable.get() == 1:
selected_country_list.append(country_name)
elif variable.get() == 0:
selected_country_list.remove(country_name)
print (selected_country_list)
def create_checkbox(parent, country_name):
country_var = tkinter.IntVar()
country_check = Checkbutton(parent, text=country_name, variable=country_var,
onvalue=1, offvalue=0,
command=lambda: select_country(country_name, country_var))
country_check.pack()
def list_countries(root):
available_country_list = ["France", "Italy"]
for country in available_country_list:
create_checkbox(root, country)
def load(root):
# Create list of country checkboxes
list_countries(root)
def start():
root = tkinter.Tk()
root.after(100, load, root) # Enable lazy loading of your components
root.mainloop()
if __name__ == '__main__':
start()

Tkinter Python 3 - Labels

I'm making a revision quiz program using Tkinter. The program will automatically get questions from an array and then output the questions using a Label. When the submit button is pressed, the program will check the answer and then should update the Label to the next question in the array but it doesn't, there is no error message. The score is updated but the Label just doesn't update.
def entryQuestion():
entryOpen = open('./files/entry.csv','r')
entryFile = csv.reader(entryOpen, delimiter = ',')
global entryQuestionArray
entryQuestionArray = []
for topic, question, answer in entryFile:
for i in range(0,1):
entryQuestionArray.append(question)
entryQuestionArray = random.sample(entryQuestionArray, len(entryQuestionArray))
arrayLength = len(entryQuestionArray)
for x in entryQuestionArray:
global qOut
qOut = x
global entryQuestion
entryQuestion = Label(entryQ, text = x)
entryQuestion.grid(row = 1, column = 0, columnspan = 3)
global answerEntry
answerEntry = StringVar()
global answerEntryBox
answerEntryBox = Entry(entryQ, textvariable = answerEntry)
answerEntryBox.grid(row = 2, column = 0, columnspan = 3)
submitEntryAnswer = Button(entryQ, text = 'Submit Answer', command = entryQuestionCheck)
submitEntryAnswer.grid(row = 3, column = 2)
def entryQuestionCheck():
entryOpen = open('./files/entry.csv','r')
entryFile = csv.reader(entryOpen, delimiter = ',')
tempScore = score
for topic, question, answer in entryFile:
if qOut == question:
if answerEntry.get() == answer:
tempScore = tempScore + 1
else:
tempScore = tempScore + 0
return
Can anyone help?
Each Widget only needs 1 call, on the top or in a main class, you need to call him only once:
class MainPage(#values):
self.label1 = tk.Label(self, #values)
and in the function, if you need to change your values, for example the text, you only need to pass self values to function, and change him, like this:
def changeValues(self):
self.label["text"] = "the text that u want to put in"
or:
def changeValues(label):
label["text"] = "the text that u want to put in"
and in the main class, or in the scope, you need to call the function with the values, if is in the scope (not in a class) you need to pass the widget in the values:
class MainPage(#values):
self.label1 = tk.Label(self, #values)
changeValues(self)
or:
label1 = tk.Label(self, #values)
changeValues(label1)

Pausing from within a tkinter function

I'm trying to create a couple of functions which do things in a sequential order. First they need to open a new window and display a label, then they need to wait for some seconds, then they need to call another function. However, I'm struggling to get the functions to wait, all the methods I've tried (.after, .sleep, .wait_visibility) seem to be ignored and it just skips to the next function call without pausing.
Here's what I have (sorry if it's messy, I'm new to python):
from tkinter import *
import time
root =Tk()
root.geometry('600x600')
def scale_screen(event = None):
global s_screen
s_screen = Toplevel(root)
s_screen.title('Residual Inhibition Tester')
s_screen.geometry('600x600')
s_screen.transient(root)
s_screen.bind('<Return>', sel)
global var
var = IntVar()
scale = Scale(s_screen, variable = var, orient = HORIZONTAL, length = 1000)
scale.focus_set()
scale.pack(anchor=CENTER)
button = Button(s_screen, text="Select", command=sel)
button.pack(anchor=CENTER)
def sel(event = None):
label = Label(s_screen)
selection = "Value = " + str(var.get())
label.config(text = selection)
interval_screen()
def interval_screen():
global i_screen
i_screen = Toplevel(root)
i_screen.geometry('600x600')
i_screen.transient(root)
i_label = Label(i_screen, text = "Please Wait")
i_label.pack(anchor = CENTER)
s_screen.destroy()
i_screen.after(3000, masker_screen)
#time.sleep(3)
#i_screen.after(300,i_label.configure(text="Playing New Masker Noise"))
#root.wait_visibility(window = i_screen)
def masker_screen():
global m_screen
m_screen = Toplevel(root)
m_screen.geometry('600x600')
m_screen.transient(root)
m_label = Label(m_screen, text = "Playing New Masker Noise").pack(anchor = CENTER)
m_screen.after(3000, lambda: scale_screen(event = None))
i_screen.destroy()
b1 = Button(root, command = scale_screen).pack(anchor=CENTER)
root.bind('<Return>', scale_screen)
root.mainloop()
In this example, the program will run but just skip the interval_screen entirely and just do the masker_screen. I'm also not averse to just using one screen and using the .configure methods to change the label text if that's easier.
Thanks!
Without seeing all the ways you tried it, it's impossible to know what you did wrong. In general you should never call time.sleep and you should never call after with just a single argument. Also, when you use after with two arguments, the second argument must be a reference to a function.
The proper way to do this is to have your first function call your second function via after:
def interval_screen():
...
i_screen.after(3000, maker_screen)
def masker_screen():
...
m_screen.after(3000, lambda: scale_screen(event = None))
Note that in your updated question you're using after incorrectly:
m_screen.after(3000, scale_screen(event = None))
You're calling the function scale_screen(...) immediately, and giving the result of that to the after function. If you need to pass arguments to your function you must create another function that does not require arguments. The simplest way to do this is with lambda, though you can also use functools.partial or you can create your own function.

Python Tkinter label widget doesn't update

Here's a piece of simplified code which doesn't work as i want it to:
def get_tl(self,x):
self.var_tl = IntVar()
if x == "Random (max = 6)":
self.var_tl.set(randint(1,6))
else:
ask_tl = Toplevel()
def destroy_t_set_tl():
self.var_tl.set(entry_tl_t.get())
ask_tl.destroy()
label_tl_t = Label(ask_tl, text="length:").pack(side=LEFT)
entry_tl_t = Entry(ask_tl, width=25)
entry_tl_t.pack(side=LEFT)
button_enter_tl_t = Button(ask_tl, text="Enter", command=destroy_t_set_tl).pack(side=LEFT)
self.label_tl = Label(self, text="length:").grid(row=1,column=0)
# This only shows the right number when "Random (max = 6)". When "Manual" it shows 0
self.show_tl = Label(self, text=self.var_tl.get()).grid(row=1,column=1)
def get_values(self):
# This always shows the right number.
self.total_label = Label(self, text=self.var_tl.get()).grid(row=4,column=0)
The function get_tl is called by an OptionMenu widget which gives x the values: "Manual" or "Random (max = 6)".
When this function is called I want it to choose a random number or open a Toplevel window which ask the user a number through an Entry. After the random number is chosen or the user has given a number. The number needs to be displayed as a label so the user can see if the number is correct.
The label only show the right number when "Random (max = 6)". When "Manual" it shows 0
After a button is pressed the function get_values is called. This however does give the right number regardless if it is manual or random.
I'm probably making a simple mistake here. But I fail to see it.
In this part:
def get_tl(self,x):
self.var_tl = IntVar()
You're recreating the variable over and over again, so it holds the default value of 0, as explained in the documentation:
VALUE is an optional value (defaults to 0)
Then you set the variable only if x == "Random (max = 6)", so in all other cases it will remain at its default.
Possibly you want to remove this line:
self.var_tl = IntVar()
You should have it only in the constructor of your class. Then all your methods will share the same instance pointed by self.var_tl.
This seems to be the answer to my own question:
def get_tl(self,x):
def tl():
self.label_tl = Label(self, text="length:").grid(row=1,column=0)
self.show_tl = Label(self, text=self.var_tl.get()).grid(row=1,column=1)
if x == "Random (max = 6)":
self.var_tl.set(randint(1,6))
tl()
else:
ask_tl = Toplevel()
def destroy_t_set_tl():
self.var_tl.set(entry_tl_t.get())
ask_tl.destroy()
tl()
label_tl_t = Label(ask_tl, text="length:").pack(side=LEFT)
entry_tl_t = Entry(ask_tl, width=25)
entry_tl_t.pack(side=LEFT)
button_enter_tl_t = Button(ask_tl, text="Enter", command=destroy_t_set_tl).pack(side=LEFT)
def get_values(self):
self.total_label = Label(self, text=self.var_tl.get()).grid(row=4,column=0)
Now both the option "Manual" and "Random" will call the function tl() which will show the number so the user can check it.
I also moved the self.var_tl = IntVar() to the constructor of the class. It might not be the optimal solution but for me it works.

Categories

Resources