Python Tkinker, checkbox append string value to list? - python

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()

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!

Change text of a specific tkinter Label using function with arguments

I'm trying to reference a specific tkinter Label in a function based on an argument input. I've tried many things, and found some topics on exec and eval and variable variables, but I want to steer away from bad practices (I don't know how to achieve it through those methods anyway). I feel like I'm missing something extremely basic, but I can't wrap my head around it.
Below is a simplified code of my function:
def myFunction(input_num):
while ArbitraryVariableName == False:
# Do stuff in while loop
if input_num== "1":
self.lbl1["text"] = "New Value 1"
elif input_num == "2":
self.lbl2["text"] = "New Value 2"
elif input_num== "3":
self.lbl3["text"] = "New Value 3"
elif input_num== "4":
self.lbl4["text"] = "New Value 4"
elif input_num== "5":
self.lbl5["text"] = "New Value 5"
# And so forth for 20+ more elif statements
You will notice that the input_num directly relates to the specific tkinter Label name of "lbl + input_num". If it helps, below is the code for one of two of the labels (they all follow a similar pattern):
self.lbl1 = Label(topframe, text="Old Value Test 1")
self.lbl1 .grid(column=1, row=1)
self.lbl2 = Label(topframe, text="Old Value Test 2")
self.lbl2 .grid(column=1, row=2)
# And so forth
Is there a cleaner and less-repetitive way to do this?
You say that you don't want to have to use the eval function, so you could instead use a label list, which makes your code rather a lot shorter:
import tkinter as tk
class example:
def __init__(self, master):
self.master = master
self.lbl1 = tk.Label(self.master, text="Old Value Test 1")
self.lbl1.grid(column=0, row=0)
self.lbl2 = tk.Label(self.master, text="Old Value Test 2")
self.lbl2.grid(column=0, row=1)
self.lbls = [self.lbl1, self.lbl2]
self.myfunction(1)
self.myfunction(2)
def myfunction(self, input_num):
self.lbls[input_num - 1]["text"] = f"New Value {input_num}"
def main():
root = tk.Tk()
example_win = example(root)
root.mainloop()
if __name__ == '__main__':
main()
With this code I did assume you had an integer from the input_num variable, instead of the string you showed in your example.
If you aren't using Python 3 you can't take advantage of the f-string.
Hope this helps,
James

Python Tkinter - get selection on Radiobutton

I need to retrieve the value of Radiobutton clicked and then use this value .
What is the way to retrieve the value of a Radiobutton clicked ?
the code to setup the Radiobutton is:
radio_uno = Radiobutton(Main,text='Config1', value=1,variable = 1)
radio_uno.pack(anchor=W,side=TOP,padx=3,pady=3)
radio_due = Radiobutton(Main,text='Config2', value=2,variable =1)
radio_due.pack(anchor=W,side=TOP,padx=3,pady=3)
radio_tre = Radiobutton(Main,text='Config3', value=3,variable = 1)
radio_tre.pack(anchor=W,side=TOP,padx=3,pady=3)
This is one solution:
Create a tk.IntVar() to track which button was pressed. I'm assuming you did a from tkinter import *.
radio_var = IntVar()
You'll need to change the way you declared your buttons:
radio_uno = Radiobutton(Main,text='Config1', value=1,variable = radio_var)
radio_due = Radiobutton(Main,text='Config2', value=2,variable = radio_var)
radio_tre = Radiobutton(Main,text='Config3', value=3,variable = radio_var)
Then use the get() method to view the value of radio_var:
which_button_is_selected = radio_var.get()
Then you can make an enum or just three if clauses that'll do stuff depending on which button is chosen:
if(which_button_is_selected == 1):
#button1 code
elif(which_button_is_selected == 2):
#button2 code
else(which_button_is_selected == 3):
#button3 code

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)

Python GUI - entry values are not strings but methods?

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.

Categories

Resources