Elegant solution about class and referencing - python

I've recently started using classes in my programs and I've got 2 problems/questions.
Following the question I've pasted not the whole code, but only the part about which I need advise about.
1)the only way I have found to access to the value the user will input in my GUI's entry widget is to declare the list ac global, so I can later append the various entry widget for further reference. I tried it in my printer function and it worked. I was just wondering if there is another way to manage the problem because using a global variable, according to what I read on the net, is not the very best idea.
2)I've got some problem with doing the same thing with the optionwidget. I can't find a way to get the data because of variable referencing problems. Assuming that I'm managing this thing the best way an apprentice of Python could, how can I do that?
If I wasn't clear, please let me know. I'm not a native English speaker and I still have to get confidence with the language.
from tkinter import *
root = Tk ()
root.title("GENERATOR")
root.geometry("300x300")
class entryandlabel:
global ac
ac=[]
def printer():
#as an example
print(ac[2].get())
def createentry(lista):
#entry widget creation
i=1
for a in lista:
entry = Entry (root)
entry.grid(row=i, column=1, sticky = W)
ac.append(entry)
i+=1
def createoption(lista, c):
#option widget creation
a = StringVar(root)
a.set("Selezionare")
b = OptionMenu(*(root, a) + tuple(lista))
b.grid(row=c, column=1)

Related

Create a function that ends mainloop and starts new one in tkinter

I'm writing my first GUI program today using Tkinter and I have stumbled onto a problem. I am trying to make a game that starts with an introduction window that closes after you press a button, then opens a new window where you can choose one of two modes. Unfortunately, I just can't get it running. It looks a little something like this.
#These are the functions that I defined to make it work
def start():
root.destroy()
def Rules_mode_1():
root.destroy
rules1 = Tk()
understood1 = Button(rules1, text="I understood", command="Start_game_mode_1")
understood.pack()
rules1.mainloop
# I haven't added rules 2 yet cause I couldn't get it to work with rules 1 so I haven't even #bothered but it would be the same code just switching the 1 for a 2. But really it isn't even
#necessary to have 2 different rule functions because the rules are the same but I couldn't think
#of another way to go about it. if you have an idea let me know
def Start_game_mode_1():
rules1.destroy #<----- THIS IS WHERE THE PROBLEM LIES. JUST DOESN'T RUN
gamemode1 = Tk()
#Here I would have the game
gamemode1.mainloop()
#now same here don't have gamemode 2 yet cause it just doesn't work yet
#This is where it really starts
root = Tk()
startbutton = Button(root, text="Start", command=start)
startbutton.pack
root.mainloop
root = Tk()
def mode():
mode1 = Button(root, command=Rules_mode_1)
mode1.pack
mode2 = #Buttonblablabla
mode()
root.mainloop()
Now I've been trying around for hours, trying to give the mainloops different names. For example giving the
rules1.mainloop
#the name
root.mainloop
but that obviously didn't work. I tried it with dozens of helper function and with the lambda expression and did hours of research but just can't seem to fix it. Does anybody have any ideas? Please be respectful and keep in mind it's my first time using Tkinter.
Thank you for your help!
After the comments didn't really help me I just tried things out for hours and in case anybody ever is having a a similar problem and reads this: The rules1 variable is inside a function, and therefore only local, which means it can't be destroyed in another function. I fixed it by making it a global, like:
def Rules_mode_1():
root.destroy
global rules1
rules1 = Tk()
understood1 = Button(rules1, text="I understood", command="Start_game_mode_1")
understood.pack()
rules1.mainloop
After that I could destroy the mainloop in the next function.

Having trouble updating int label but can update string label fine

I'm sure someone is going to mark this as a duplicate the second I post it, but I assure you, I've been looking for hours. I'm brand new to tkinter so bear with me as I'd like a really straightforward answer if possible. Anything related to this specifically was a bit complex for me right now and I didn't feel it answered my question.
I know how to have a Label update with textvariable and StringVar. However, I'm trying to update an integer and I can't seem to figure it out for some reason. The number updates and prints through the console but I can't figure out the right syntax to get it to show up on the interface. It either just shows 0 (as the default variable shows) or there is no text there at all depending on what I've changed the code to)
So all I'm doing is simply incrementing a number. Let's leave it at that for now. And if anyone has any resources to more straightforward documentation please let me know because it seems tkinter in general is pretty obscure in documentation online as far as I can tell.
my_count = 0
def increase_mycount():
global my_count
increment = int(my_count) + 1
my_count = str(increment)
print(my_count)
Label(root, textvariable=my_count).grid(row=2, column=1)
Button(root, text="+", command=inc_mycount).grid(row=3, column=2)
This is where it's at right now, I've tried changing my_count into an IntVar and also a StringVar and I get an error saying I can't use + with int and intvar or int with stringvar
Is there something really simple I'm missing? I'm struggling finding comprehensive documentation on tkinter. It's easy to find Python information but not this really.. I'm in the process of organizing all the info I'd like into some google docs.
Thank you for any time you give. This seems like it should be a really simple thing to do but I've only worked with engines that update things for me. I'm only use tkinter, a .py file, and cmd for this.
And another note, I can't use .set() for this either it seems, like I could for a string. So I'm just struggling with the syntax unless there is a different method for numbers on labels.
You have to use one of tkinter's variable objects when using textvariable. In this case, IntVar.
import tkinter as tk
root = tk.Tk()
my_count = tk.IntVar()
def increase_mycount():
current = my_count.get()
my_count.set(current+1)
tk.Label(root, textvariable=my_count).grid(row=2, column=1)
tk.Button(root, text="+", command=increase_mycount).grid(row=3, column=2)
root.mainloop()

Python Tkinter open multiple windows within a function

I have an inbuilt calculator within my program. My main program was designed to be somewhat similar to Microsoft Bob. Now when the user presses the 'Calculator' Button in the main focus, it opens up the part of the calculator where you define your 2 numbers and the operator. When you press 'Results' the answer does not appear in any shape of form until my main program is closed. Any help? Attached is the calculator code sample, and more than happy to change the second window to something else. BTW this is for a school project.
def Calculator():
calculator = Tk()
Number1 = DoubleVar()
Number2 = DoubleVar()
Operator = IntVar()
Operator.set(1)
Entry(calculator,textvariable=Number1,justify="c").grid()
Entry(calculator,textvariable=Number2,justify="c").grid()
Radiobutton(calculator,text="Add",variable=Operator,value=1).grid()
Radiobutton(calculator,text="Subtract",variable=Operator,value=2).grid()
Radiobutton(calculator,text="Multiply",variable=Operator,value=3).grid()
Radiobutton(calculator,text="Divide",variable=Operator,value=4).grid()
Radiobutton(calculator,text="Square",variable=Operator,value=5).grid()
Radiobutton(calculator,text="Square root",variable=Operator,value=6).grid()
Button(calculator,text="results",command=calculator.destroy,width=16).grid()
Number1=Number1.get()
Number2=Number2.get()
Operator=Operator.get()
if Operator==1:Results=Number1+Number2
if Operator==2:Results=Number1-Number2
if Operator==3:Results=Number1*Number2
if Operator==4:Results=Number1/Number2
if Operator==5:Results=math.pow(Number1,Number2)
if Operator==6:Results=Number1*(1/Number2)
Results = "The answer is "+str(Results)
Answer = Tk()
Answer.geometry("150x150")
Label(Answer, text=Results).place(relx=.5,rely=.5,anchor="center")
Answer.mainloop()
You can't have multiple Tk objects with their own mainloops.
Or, rather, you can, but whichever one is currently running mainloop, none of the others (and none of their children) gets to do anything until that mainloop finishes.
What you want is to have multiple Toplevel widgets, with the same Tk as their master. (If you just have one Tk, as you usually do, you can leave that as the default.)
But you have another, equally serious problem here.
You're creating a Calculator with a bunch of Tk vars attached, then immediately trying to read those vars and do something with them. You can't do that.
What you need to do is to put all of that in the callback to some kind of user event, like clicking the Results button.
You have one more problem in your code that makes things a bit harder:
Number1=Number1.get()
What you really want here is two separate variables, one the Tk var, and the other an int. And then you need to make the Tk var accessible in the results callback in some way. The obvious way is to move all of this to a class, and store all your Tk vars as instance attributes. If you don't know how to do that, you can always use globals. (Not ideal, but until you learn classes, it's fine.) Then the actual numbers are just local to the callback function.
Putting it all together:
def Calculator(root):
global Number1, Number2, Operator
calculator = Toplevel()
Number1 = DoubleVar()
Number2 = DoubleVar()
Operator = IntVar()
Operator.set(1)
Entry(calculator,textvariable=Number1,justify="c").grid()
Entry(calculator,textvariable=Number2,justify="c").grid()
Radiobutton(calculator,text="Add",variable=Operator,value=1).grid()
Radiobutton(calculator,text="Subtract",variable=Operator,value=2).grid()
Radiobutton(calculator,text="Multiply",variable=Operator,value=3).grid()
Radiobutton(calculator,text="Divide",variable=Operator,value=4).grid()
Radiobutton(calculator,text="Square",variable=Operator,value=5).grid()
Radiobutton(calculator,text="Square root",variable=Operator,value=6).grid()
Button(calculator,text="results", command=lambda event: calculate(calculator), width=16).grid()
def calculate(calculator):
n1=Number1.get()
n2=Number2.get()
op=Operator.get()
if op==1:Results=n1+n2
if op==2:Results=n1-n2
if op==3:Results=n1*n2
if op==4:Results=n1/n2
if op==5:Results=math.pow(n1,n2)
if op==6:Results=n1*(1/n2)
Results = "The answer is "+str(Results)
calculator.destroy()
Answer = Toplevel()
Answer.geometry("150x150")
Label(Answer, text=Results).place(relx=.5,rely=.5,anchor="center")
root = Tk()
# Presumably your real code has some top-level stuff, where the
# user can ask you to open a calculator, like a button whose
# command calls the Calculator function? But here, we'll just:
Calculator(root)
root.mainloop()

page GUI - Combobox pass variable

New to GUI. Not quite getting there. I used page and get can get buttons to do something (click on a button and get a response). With Combobox, I can't pass a value. Searched here, tried many things, watched a few hours of youtube tutorials.
What am I doing wrong below? This is the code page generates (basically) then I added what I think I need to do to use the Combobox.
I am just trying to have 1,2,3 in a combo box and print out the value that is chosen. Once I figure that out I think I can actually make a simple GUI that passes variables I can then program what I want to do with these variables being selected.
class New_Toplevel_1:
def __init__(self, top):
self.box_value = StringVar()
self.TCombobox1 = ttk.Combobox(textvariable=self.box_value)
self.TCombobox1.place(relx=0.52, rely=0.38, relheight=0.05, relwidth=0.24)
self.TCombobox1['values']=['1','2','3']
self.TCombobox1.configure(background="#ffff80")
self.TCombobox1.configure(takefocus="")
self.TCombobox1.bind('<<ComboboxSelected>>',func=select_combobox)
def select_combobox(self,top=None):
print 'test combo ' # this prints so the bind works
self.value_of_combo = self.ttk.Combobox.get() # this plus many other attempts does not work
It's hard to know what you're actually asking about, since there is more than one thing wrong with your code. Since you say the print statement is working, I'm assuming the only problem you have with your actual code is with the last line.
To get the value of the combobox, get the value of the associated variable:
self.value_of_combo = self.box_value.get()
Here's a working version where I fixed the other things that were wrong with the program:
from tkinter import *
from tkinter import ttk
class New_Toplevel_1:
def __init__(self, top):
self.box_value = StringVar()
self.TCombobox1 = ttk.Combobox(textvariable=self.box_value)
self.TCombobox1.place(relx=0.52, rely=0.38, relheight=0.05, relwidth=0.24)
self.TCombobox1['values']=['1','2','3']
self.TCombobox1.configure(background="#ffff80")
self.TCombobox1.configure(takefocus="")
self.TCombobox1.bind('<<ComboboxSelected>>',func=self.select_combobox)
def select_combobox(self,top=None):
print('test combo ') # this prints so the bind works
self.value_of_combo = self.box_value.get()
print(self.value_of_combo)
root = Tk()
top = New_Toplevel_1(root)
root.mainloop()
Note: I strongly advise you not to start with place. You should try to learn pack and place first. I know place seems easier, but to get the most responsive and flexible GUI you should leverage the power of pack and grid.

Checking if the user presses 'Return' while selected in an Entry box Tkinter

I'm using Tkinter to create a GUI for a simple geometry calculator I'm creating.
Basically, what I have is an Entry box. What I want is for the program/GUI/system to detect when the user of the program hits the 'Enter' or 'return' key WHILE they are in the Entry box. When this is detected, I want the contents of the Entry box to be appended to a list I have defined earlier. I also want a simple label to be created on the GUI that displays the contents of the list (including the appended item(s)). Note that the list begins with nothing in it.
Here is my code so far:
from tkinter import *
#Window setup(ignore this)
app = Tk()
app.title('Geometry Calculator')
app.geometry('384x192+491+216')
app.iconbitmap('Geo.ico')
app.minsize(width=256, height=96)
app.maxsize(width=384, height=192)
app.configure(bg='WhiteSmoke')
#This is the emtry list...
PointList = []
#Here is where I define the variable that I will be appending to the list (which is the object of the Entry box below)
StrPoint = StringVar()
def list_add(event):
#I don't really know how the bind-checking works and how I would implement it; I want to check if the user hits enter while in the Entry box here
if event.char == '':
PointList.append(StrPoint)
e1 = Entry(textvariable=StrPoint).grid(row=0, column=0)
app.bind('<Return>', list_add)
mainloop()
I don't really know the proper way to check for 'Return' and then use it in an if statement.
I hope you understand what I'm trying to get help with, and I've looked all around for an explanation that I could understand with no success.
Instead of binding with the app just bind it with the Entry widget object,i.e,e1
from tkinter import *
#Window setup(ignore this)
app = Tk()
app.title('Geometry Calculator')
app.geometry('384x192+491+216')
app.iconbitmap('Geo.ico')
app.minsize(width=256, height=96)
app.maxsize(width=384, height=192)
app.configure(bg='WhiteSmoke')
#This is the emtry list...
PointList = []
#Here is where I define the variable that I will be appending to the list (which is the object of the Entry box below)
StrPoint = StringVar()
def list_add(event):
print ("hello")
#I don't really know how the bind-checking works and how I would implement it; I want to check if the user hits enter while in the Entry box here
if event.char == '':
PointList.append(StrPoint)
e1 = Entry(textvariable=StrPoint)
e1.grid(row=0, column=0)#use grid in next line,else it would return None
e1.bind('<Return>', list_add)# bind Entry
mainloop()
The solution is to set the binding on the widget itself. That way, the binding will only apply while focus is on that widget. And since you're binding on a specific key, you don't need to check for the value later. You know the user pressed return, because that's the only thing that will cause the binding to fire.
...
e1.bind('<Return>', list_add)
...
You have another problem in that your list_add function needs to call the get method of the variable rather than accessing the variable directly. However, since you aren't using any of the special features of a StringVar, you really don't need it -- it's just one more thing you have to manage.
Here's how to do it without the StringVar:
def list_add(event):
PointLit.append(e1.get())
...
e1 = Entry(app)
e1.grid(row=0, column=0)
e1.bind('<Return>', list_add)
Note that you need to create the widget and lay out the widget in two steps. Doing it the way you did it (e1=Entry(...).grid(...) will cause e1 to be None since that is what .grid(...) returns.

Categories

Resources