I am a beginner and I am making a login system (just for practicing).
I'm using tkinter to develop a simple UI. The thing is that when I call a second root (sign_in root) with a button from another root (main_screen), and I try to get some values typed in entry with StringVars assigned to them, they return just an empty string ""
def main_screen():
root=Tk()
user=StringVar()
pas=StringVar()
btn2=Button(root,text='Sign-In',command=sign_in_screen)
btn2.place(x=125,y=160)
root.mainloop()
def sign_in_screen():
root1=Tk()
newuser=StringVar()
newpas=StringVar()
ent3=Entry(root1,width=28,textvariable=newuser)
ent3.place(x=100,y=50)
ent4=Entry(root1,width=28,textvariable=newpas,show="*")
ent4.place(x=100,y=100)
btn3=Button(root1,text='Sign-In',command=lambda:register(newuser.get(), newpas.get()))
btn3.place(x=50,y=160)
root1.mainloop()
main_screen()
Having multiple instances of Tk become hideously complicated because each one creates a separate tcl interpreter. This causes weird effects like what you see here. It's you almost always want to use the Toplevel widget.
Related
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()
I created a Frame, gave it a menubar. Works just fine. The purpose of the entry in the menubar is to open a new frame, in which u can change some settings. The creation of the new Window works also. However I can't create widgets on the new created window. I tried it with a Button and got a
TclError: can't invoke "button" command: application has been destroyed
I tried to google it and found Cannot invoke button command: application has been destroyed which didn't quite helped me.
Further I found a solution were u have to create a parent class (which inherrits from Frame) and than create all other Frames within it, but on the first view it looked pretty complicated. Especially because the creation of the second window seems to work in the first place.
I know this is probably a really basic question, so thanks in advance for your time
def perfSettings():
perfFrame = Tk(className=" Performanz Einstellungen")
perfFrame.configure(bg='#F2F2F2')
perfFrame.geometry("300x300")
perfFrame.mainloop()
btn = Button(master=perfFrame, text='Speichern', command=myPerfSettingValue.getValues, width=37)
btn.pack()
# Button(perfFrame, text='Abbrechen', command=perfFrame.destroy, width=37).grid(row=0 ,column=1 )
class perfSettingsValue:
def __init__(self):
self.bvhSteps = 0
def getValues(self):
pass
#Hauptfenster
root = Tk(className="BoneMapping & SkeletonEstimation")
root.configure(bg='#F2F2F2')
root.geometry("1300x600")
myPerfSettingValue = perfSettingsValue()
menubar = Menu(root)
sdmenu = Menu(menubar, tearoff=0)
sdmenu.add_command(label="Performanz", command=perfSettings)
menubar.add_cascade(label='Einstellungen',menu=sdmenu)
root.config(menu=menubar)
The key problem here is that you are trying to add a button after starting the mainloop which effectively blocks the execution of the program. The error you are getting is because the line that adds the button gets executed after the window has been closed.
Your problem will be solved if you modify your function like this:
def perfSettings():
perfFrame = Tk(className=" Performanz Einstellungen")
perfFrame.configure(bg='#F2F2F2')
perfFrame.geometry("300x300")
btn = Button(master=perfFrame, text='Speichern', command=myPerfSettingValue.getValues, width=37)
btn.pack()
perfFrame.mainloop()
This is not the only problem though. Instead of creating a new instance of Tk, you should create a new Toplevel instance, which will, in your case, act just as a Tk instance, but have a lot less tendency to cause trouble.
Finally, you should consider reading on the object oriented approach to designing tkinter applications. There are far too many variants of that to be appropriately elaborated here but I certainly recommend you take the effort to learn to use one of them. It will make your code more comprehensible and maintainable. My usual approach is to create a class that inherits from Toplevel or Tk for every type of window I am going to use.
I'm currently trying to make a small application with a GUI that pulls weather from a website and displays the results in a window. I've got it to work without the GUI and also with the GUI but when I wrote the latter it was all in one script and not very organized. Because it was so unorganized, I decided to make make a separate script that would draw the GUI when the class was called.
Part of the GUI is an 'Entry' box that can be added via Tkinter. The entry box stores it's content into a StringVar() and that content can displayed using .get(). This works fine and well when I wrote everything unorganized into one script but I can't for the life of me figure out how to pass this StringVar() from one method to another in my program. This is what it looks like:
from Tkinter import *
import Forecast
class Frames(object):
def __init__(self):
pass
def main_frame(self):
main = Tk()
main.title('WeatherMe')
main.geometry('300x100')
query = StringVar()
Label(main, text='Enter a city below').pack()
Entry(main, textvariable=query).pack()
Button(main, text="Submit", command=self.result_frame).pack()
main.mainloop()
def result_frame(self):
result = Tk()
result.title('City')
result.geometry('600x125')
Button(result, text="OK", command=result.destroy).pack()
result.mainloop()
Basically my goal is to have one window open when the program is launched with a label, an entry box, and a submit button. When a city is entered in the entry box and submit it clicked a new window will open displaying the results.
Because the entry is on the first window I need to pass the value of entry's StringVar() to the second window so it can then pull the data and display the labels. No matter what I try it doesn't seem to work, I either get a 404 error meaning something is wrong with that string making the link it tries to get a response from invalid or a concatenate error 'cannot concatenate str and instance objects'.
I've also tried saving StringVar() as a variable outside of either method but the issue with that is I need to then call another instance of Tk() before StringVar().
You are creating two separate instances of Tk. You shouldn't do that. One reason why is because of this exact problem: you can't share instances of widgets or tkinter variables between them.
If you need more than one window, create a single root window and then one or more instances of Toplevel. Also, call mainloop only for the root window.
I'm proposing a continuation of the discussion in disable tkinter keyboard shortcut: I have an event handler for an event that Tkinter also uses, so that my prog & Tkinter interact badly.
Since it is a problem that I've been unable to solve I'm re-proposing here, where I tried to boil it down to the simplest form in the following code:
#!/usr/bin/env python
from Tkinter import *
import tkFont
def init():
global root,text
root = Tk()
root.geometry("500x500+0+0")
dFont=tkFont.Font(family="Arial", size=10)
text=Text(root, width=16, height=5, font=dFont)
text.pack(side=LEFT, fill=BOTH, expand = YES)
root.bind("<Control-b>", setbold)
text.tag_config("b",font=('Verdana', '10', 'bold' ))
text.tag_config("i",font=('Verdana', '10', 'italic' ))
def removeformat(event=None):
text.tag_remove('b',SEL_FIRST,SEL_LAST)
text.tag_remove('i',SEL_FIRST,SEL_LAST)
def setbold(event=None):
removeformat()
text.tag_add('b', SEL_FIRST,SEL_LAST)
text.edit_modified(True)
def main():
init()
mainloop()
if __name__ == '__main__':
main()
What it should do is simply to produce a text window where you write into.
Selecting some text and pressing Ctrl+B the program should remove any preexisting tag, then assign to it the 'b' tag that sets the text to bold.
What instead happens is an exception at the first tag_remove, telling me that text doesn't contain any characters tagged with "sel".
The suggestion to use a return 'break' is of no use, since the selection disappears before setbold() has any chance to act...
Set your binding on the text widget, not on the root. (Whole toplevel bindings are processed after widget class bindings – where the standard <Control-Key-b> binding is – and those are processed after the widget instance bindings, which is what you want to use here.) And you need to do that 'break'; it inhibits the subsequent bindings. (If you're having any problems after that, it's probably that the focus is wrong by default, but that's easy to fix.)
The only other alternative is to reconfigure the bindtags so that class bindings are processed after toplevel bindings, but the consequences of doing that are very subtle and far-reaching; you should use the simpler approach from my first paragraph instead as that's the normal way of handling these things.
Bindings are handled in a specific order, defined by the bindtags of that widget. By default this order is:
The specific widget
The widget class
The toplevel window
The special class "all"
If there are conflicting bindings -- for example, a control-b binding on both the widget and class -- they both will fire (in the described order) unless you break the chain by returning "break".
In the case of the code you posted, however, you are binding to the toplevel window (ie: the root window), and the conflicting binding is on the class. Therefore, the binding will fire for the class before it is processed by the toplevel, so even if your binding returned "break" it wouldn't matter since the class binding happens first.
The most straight-forward solution is to move your binding to the actual widget and return "break". That will guarantee your binding fires first, and the return "break" guarantees that the class binding does not fire.
If you really want your binding on the root window, you can remove the binding for the class using the bind_class method with the value of "Text" for the class.
You might find the Events and Bindings page on effbot.org to be useful.
I am re designing a portion of my current software project, and want to use hyperlinks instead of Buttons. I really didn't want to use a Text widget, but that is all I could find when I googled the subject. Anyway, I found an example of this, but keep getting this error:
TclError: bitmap "blue" not defined
When I add this line of code (using the IDLE)
hyperlink = tkHyperlinkManager.HyperlinkManager(text)
The code for the module is located here and the code for the script is located here
Anyone have any ideas?
The part that is giving problems says foreground="blue", which is known as a color in Tkinter, isn't it?
If you don't want to use a text widget, you don't need to. An alternative is to use a label and bind mouse clicks to it. Even though it's a label it still responds to events.
For example:
import tkinter as tk
class App:
def __init__(self, root):
self.root = root
for text in ("link1", "link2", "link3"):
link = tk.Label(text=text, foreground="#0000ff")
link.bind("<1>", lambda event, text=text: self.click_link(event, text))
link.pack()
def click_link(self, event, text):
print("You clicked '%s'" % text)
root = tk.Tk()
app = App(root)
root.mainloop()
If you want, you can get fancy and add additional bindings for <Enter> and <Leave> events so you can alter the look when the user hovers. And, of course, you can change the font so that the text is underlined if you so choose.
Tk is a wonderful toolkit that gives you the building blocks to do just about whatever you want. You just need to look at the widgets not as a set of pre-made walls and doors but more like a pile of lumbar, bricks and mortar.
"blue" should indeed be acceptable (since you're on Windows, Tkinter should use its built-in color names table -- it might be a system misconfiguration on X11, but not on Windows); therefore, this is a puzzling problem (maybe a Tkinter misconfig...?). What happen if you use foreground="#00F" instead, for example? This doesn't explain the problem but might let you work around it, at least...