Even though there is a mainloop being called my tk window will not appear. The code used to work but as soon as I coded in the second function in the nums class there is no tk window. I would like for someone to point out the mistake instead of simply handing out the answer.
Can someone please help me fix this problem?
I use Python IDLE 3.8
Image: [1]: https://i.stack.imgur.com/o65WI.png
Code:
from tkinter import *
from random import randint
import time
#number assignments
class nums:
def __init__(self):
self.value=randint(1,100)
def assignnewnums(oldnum1,oldnum2,lbltxt,lbl,answer):
getans = answer.get()
if(getans==str((oldnum1.value+oldnum2.value))):
del(oldnum1)
del(oldnum2)
oldnum1=nums()
oldnum2=nums()
lbltxt="Correct!"
lbl.config(text=lbltxt)
time.sleep(5)
lbltxt="What is {} + {}".format(oldnum2.value,oldnum1.value)
lbl.config(text=lbltxt)
else:
lbltxt="Wrong! Try Again!"
lbl.config(text=lbltxt)
time.sleep(3)
lbltxt="What is {} + {}".format(oldnum2.value,oldnum1.value)
lbl.config(text=lbltxt)
a = nums()
b = nums()
#GUI startup
root = Tk()
#Label
title = Label(root, text="AddPrac", fg="dark blue")
title.pack()
#Question
questxt = "What is {} + {}".format(a.value,b.value)
ques = Label(root,text=questxt,fg="red")
ques.pack()
#UserAnswer
ans = Entry(root)
ans.pack()
#SubmitButton
enter = Button(root,text="Submit Answer!",fg="yellow",command=nums.assignnewnums(a,b,questxt,ques,ans))
enter.pack()
#GUI continued startup
root.mainloop()
I tried your code and the window does appear if you wait a few seconds.
This is due to the following offending code snippet:
command=nums.assignnewnums(a,b,questxt,ques,ans)
This doesn't do what you think it does. You were thinking of:
command=lambda: nums.assignnewnums(a, b, questxt, ques, ans)
The way your code is written now, it does not bind a callback to the button, but rather, calls- and executes the function (since you are invoking it explicitly), and attempts to bind the return value as a callback, which makes no sense. As a side effect of calling the function, the main thread sleeps (since assignnewnums uses time.sleep) for a bit before you reach root.mainloop.
Anytime you are binding a callback to a button, you want to provide a callable object - either just a function object, or if arguments are critical, a lambda or functools.partial.
Related
I'm trying to replace sleep() with after() but I need to create a function which will give me a return value that I can store and I can't figure out how. Let's take this code :
import tkinter
root = tkinter.Tk()
def test(i):
o=i*2
return o
print(root.after(5000,test,6))
root.mainloop()
This results in this output:
after#2914 which is a string
What can I do? I tried storing the return of the function in a first variable like so:
v=test(6)
print(root.after(5000,v))
but this error pops up:
'int' object has no attribute '__name__'
I also tried using threading instead of after() but it doesn't solve the initial problem (the tkinter window stops responding during sleep).
It will easier for you/
import tkinter
root = tkinter.Tk()
def test(i):
o=i*2
label.configure(text=o)
return o
root.after(5000,test,6)
label = tkinter.Label(root, text="Result will be shown here")
label.pack()
root.mainloop()
test(6)
Screenshot before countdown:
Screenshot after countdown to 6 seconds:
I have written some code primarily to be used with the console, but was asked to create a simple GUI for it for ease of use. In it, I am setting up the main frame with widgets, and using the widget command to call upon the function that I import. However, the imported functions and modules all write to the output console. Is there a means of returning the output string/console output to be updated in the GUI as the subprocess runs?
Example script:
import Tkinter as *
import myModule1
class MyGUI(Frame):
def __init__(self):
# Initialization stuff.
self.initGUI()
def initGUI(self):
self.downloadButton = Button(text="Download data.")
self.downloadButton["command"] = myModule1.function
# This function from myModule1 will constantly print to the console as the process is performed - is there any way to have this displayed in the GUI as a ScrollBar?
.....
Alternatively, is there a way to make a dialog window show up while the process is running? I ask because I have embedded a lot of print statements in the myModule1 and its submodules that return what is going on to the console. It would be nice to display those for the users one the GUI is working and I convert it to a .exe for ease of use of those who will be using it.
Thank you.
EDIT: An example of what myModule1.function can look like:
import otherModule
def function1():
log = otherModule.function2():
if log == True:
print "Function 2 worked."
elif log == False:
print "Function 2 did not work."
However, function2 in otherModule prints to console as it performs its calculations. That is not explicitly shown here, but the console output would essentially be a series of calculations followed by the example if/elif clause shown above.
It won't be extremely simple, but one way to do this is create a method or function in your GUI that writes to a text widget or some other widget whose content can be updated easily. I'll call it outputMethod. Then, pass this function or method to myModule1.function(outputMethod). Within the module1.function, replace print statements with the outputMethod call, providing the appropriate parameters to it. Without seeing module1.function, it's difficult to provide a complete example that would work. Added the following example once the OP posted myModule1 sample code.
from Tkinter import *
import myModule1
class MyGUI(Frame):
def __init__(self, parent):
# Initialization stuff.
self.initGUI(parent)
def initGUI(self, parent):
Frame.__init__(self, parent)
self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure(1, weight=1)
self.pack(expand='yes', fill='both')
self.downloadButton = Button(self, text="Download data.")
self.downloadButton["command"] = lambda m=self.outputMethod: myModule1.function(m)
self.text = Text(self)
self.downloadButton.grid(row=0, column=0)
self.text.grid(row=1, column=0, sticky='news')
self.sy = Scrollbar(self, command=self.text.yview)
self.text['yscrollcommand'] = self.sy.set
self.sy.grid(row=1, column=1, sticky='nsw')
def outputMethod(self, the_output):
self.text.insert('end', the_output + '\n')
# Scroll to the last line in the text widget.
self.text.see('end')
if __name__ == '__main__':
# Makes the module runable from the command line.
# At the command prompt type python gui_module_name.py
root = Tk()
app = MyGUI(root)
# Cause the GUI to display and enter event loop
root.mainloop()
Now for module1...
def function(log):
# Note that log is merely a pointer to 'outputMethod' in the GUI module.
log("Here is a string you should see in the GUI")
log("And this should appear after it.")
# Now demonstrate the autoscrolling set up in outputMethod...
for i in range(50):
log("Inserted another row at line" + str(i + 2))
Everyone! I first of all apologize for my lack of coding knowledge, I am currently attempting to learn Python on my own for "fun." My only formal education comes from a high-school Java AP course taken years ago.
I am currently using Python version 3.6 on the Windows 10 operating system, utilizing the PyCharm IDE.
On run my Tkinter GUI based application automatically executes an exit function that I defined under a class. The desired effect is for the window to close only when the user clicks the "terminate" button within the GUI window.
My code so far is as follows:
import webbrowser
import tkinter as ttk
from PIL import ImageTk, Image
##Application main window setup.
window = ttk.Tk()
window.maxsize(width=200, height=200)
window.minsize(width=200,height=200)
window.config(bg=("black"))
window.title("Hello World")
##Set a 'class' for exit function of application.
class Exit():
##Defines the countdown timer and sets parameters that need to be satisfied before exit.
def timer(self):
countdown = 3
self.x = int
for self.x in reversed(range(0,countdown + 1)):
print(self.x)
##When 'x' reahces -1 the application exits.
if self.x > -1:
print("Bye!")
window.destroy()
##Otherwise a label displaying a text message appears.
else:
swell = ttk.Label(text=("'Hello World!'"),bg=("black"),fg=("white"),font=("Times New Roman",12,"bold"))
swell.place(x=50,y=50)
##Retrieve the defined 'timer' function from the 'Exit' class.
exit=Exit()
exit.timer()
##Button with attahced command to execute the exit of application via user input.
quitButton=ttk.Button(
window,text=("Terminate"),bg=("red"),fg=("white"),font=("bold"),width=20,height=1,anchor=ttk.S,command=lambda: exit)
quitButton.place(x=6,y=150)
window.mainloop()
Any form of help is appreciated, and I thank you in advance.
*As a side note I can successfully issue a command from a button, however the retrieved function is only one line. It seems I cannot handle multiple lines of code.
I think what is happening is that you are destroying the window in the timer class method. After your for loop, x will equal 0. Therefore it is more than -1, and the window class is destroyed. Quitbutton trys to use window but it has been destroyed.
In the output I assume you are seeing 'Bye'
I got the correct result with the following:
import tkinter as ttk
from time import sleep
##Application main window setup.
window = ttk.Tk()
window.maxsize(width=200, height=200)
window.minsize(width=200, height=200)
window.config(bg=("black"))
window.title("Hello World")
##Set a 'class' for exit function of application.
class Exit():
"""
Defines the countdown timer and sets parameters
that need to be satisfied before exit.
"""
def __init__(self):
self.countdown = 3
swell = ttk.Label(text=("Hello World!"), bg=("black"),
fg=("white"), font=("Times New Roman", 12, "bold"))
swell.place(x=50,y=50)
def quit(self):
for iteration in reversed(range(0, self.countdown + 1)):
print(iteration)
sleep(1)
print("Bye!")
window.destroy()
##Retrieve the defined 'timer' function from the 'Exit' class.
exit=Exit()
##Button with attahced command to execute the exit of application via user input.
quitButton=ttk.Button(
window,text=("Terminate"), bg=("red"), fg=("white"), font=("bold"),
width=20, height=1, anchor=ttk.S, command=lambda: exit.quit())
quitButton.place(x=6,y=150)
window.mainloop()
You can see here I also used the init method in the exit class. Its a special kind of method which will auto run when the class is initiated.
It didn't require much changing. All I did was move the destroy window function into its own class method and had the second window instance command be set to run this method.
I've got an interesting problem with the tk Entry widget. If I run the following test code,
from Tkinter import *
root =Tk()
def pfunc(self):
print Input.get()
f=Frame(root)
f.pack()
Input=Entry(f)
#Input.bind("<Return>",pfunc)
Input.pack()
root.mainloop()
I can properly enter into the widget and print to console; however the following code, as part of a larger GUI, does not allow me to click in the Entry boxes at all.
self.Tlabel = Label(self.TempFrame, text="Temp")
self.Tlabel.pack( side = LEFT)
self.Tenter = Entry(self.TempFrame,width=10, bd =5)
self.Tenter.bind("<Return>",self.getFlux)
self.Tenter.pack (side=RIGHT)
self.Flabel = Label(self.FluxFrame, text="Flux")
self.Flabel.pack( side = LEFT)
self.Fenter = Entry(self.FluxFrame, width=10, bd =5)
self.Fenter.bind("<Return>",self.getTemp)
self.Fenter.pack(side = RIGHT)
def getFlux(self):
for i in range(len(self.fit_tuples)):
if self.fit_tuples[i][0]==self.currentBFMdate and self.fit_tuples[i][1]==self.cell.get():
fit_data=self.fit_tuples[i][2]
self.Fenter.set(fit_data[0]*np.exp(fit_data[1]*int(self.Tenter.get())))
else:
self.Fenter.set("Invalid")
def getTemp(self):
for i in range(len(self.fit_tuples)):
if self.fit_tuples[i][0]==self.currentBFMdate and self.fit_tuples[i][1]==self.cell.get():
fit_data=self.fit_tuples[i][2]
self.Tenter.set(np.log(float(self.Fenter.get())/fit_data[0])/fit_data[1])
else:
self.Tenter.set("Invalid")
Furthermore, if I run both codes on a separate windows PC I have the same problem. The only difference I can possibly think of is that I am using instance variables within a class; but it seems that other widgets are bound and working properly.
Basically, the bind method is passing "Return" as a parameter to getTemp. As another user suggested, just add another parameter to the function.
If you use bind method, callback is called with an event object. You should add event parameter to the method/function. (See Events and Bindings - An Introduction to Tkinter )
So, rpelcae following lines:
def getFlux(self, event):
...
def getTemp(self, event):
...
The first program work unintentionally. Its parameter name should be event, not self.
Goal
I am trying to write a basic file which I can import in all other programs that will have a simple function that will take entry from the user and then return it.
Code
For that I have the following code:
class takeInput(object):
def __init__(self,requestMessage,parent):
self.string = ''
self.frame = Frame(parent)
self.frame.pack()
self.acceptInput(requestMessage)
def acceptInput(self,requestMessage):
r = self.frame
k = Label(r,text=requestMessage)
k.pack(side='left')
self.e = Entry(r,text='Name')
self.e.pack(side='left')
self.e.focus_set()
b = Button(r,text='okay',command=self.gettext)
b.pack(side='right')
def gettext(self):
self.string = self.e.get()
self.frame.destroy()
print self.string
def getString(self):
return self.string
def getText(requestMessage,parent):
global a
a = takeInput(requestMessage,parent)
return a.getString()
And I also added some script level code so as to test this:
root = Tk()
getText('enter your name',root)
var = a.getString()
print var
root.mainloop()
And what is really baffling me is that:
var does not have the value that I entered it has the empty string ''
a.string variable has the value that I entered and I checked this from the shell.
Also When I tried to assign the string returned from a.getString() to var in the shell, then it worked.
note I am new to Tkinter programming and dont fully understand how the mainloop() works. So maybe this is were the problem is. But I am not sure.
Specs
OS:Linux Mint 14
Python IDLE 2.7
Please help me out with this issue.
As other answers tell, you print var before entering the mainloop, that is, before your window is actually running, and your program is waiting for user input.
You could rely on tkSimpleDialog family to get your input:
import Tkinter
import tkSimpleDialog
root = Tkinter.Tk()
var = tkSimpleDialog.askstring("Name prompt", "enter your name")
print var
If you want to pursue your way, you could perform your print from the "ok" button callback (gettext in your case). You could also generate a virtual event when "ok" is pressed and bind to this event in your main program (http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/virtual-events.html)
The flow of your code goes like this:
the main scope calls getText.
getText creates a takeInput object a.
the takeInput object initializes itself, creating Labels & buttons etc.
getText returns a.getString(), which returns self.string, which still has its default value, the empty string.
the main scope prints var, which is empty.
So far, all of this has taken place within the span of a few nanoseconds. The user hasn't even seen the window yet.
the main scope then calls root.mainloop(), which finally gives the user the opportunity to interact with the window. But it's too late. var has already been printed.
If you want getText to not return until the user has submitted his text, then mainloop has to occur inside getText, not after it.
from Tkinter import *
class takeInput(object):
def __init__(self,requestMessage):
self.root = Tk()
self.string = ''
self.frame = Frame(self.root)
self.frame.pack()
self.acceptInput(requestMessage)
def acceptInput(self,requestMessage):
r = self.frame
k = Label(r,text=requestMessage)
k.pack(side='left')
self.e = Entry(r,text='Name')
self.e.pack(side='left')
self.e.focus_set()
b = Button(r,text='okay',command=self.gettext)
b.pack(side='right')
def gettext(self):
self.string = self.e.get()
self.root.destroy()
def getString(self):
return self.string
def waitForInput(self):
self.root.mainloop()
def getText(requestMessage):
msgBox = takeInput(requestMessage)
#loop until the user makes a decision and the window is destroyed
msgBox.waitForInput()
return msgBox.getString()
var = getText('enter your name')
print "Var:", var
The problem is that your test routine already prints out the value of var before the dialog has been shown, let alone text being entered. (You can easily validate this by adding some print statements to your test code.) This is because the call to mainloop() is at the very end. Instead, you should call mainloop after creating the frame, but before reading and returning the input, e.g. it might go to your getText method:
def getText(requestMessage,parent):
a = takeInput(requestMessage,parent)
parent.mainloop()
return a.getString()
This still does not work really well, as you have to close the dialog (click the [x]-button) even after clicking on 'okay', and I am not sure how to fix this.
However, note that there already is a module for this, tkSimpleDialog, providing methods such as askstring(title, prompt) that show just such an input dialog. So you might either use those, or look at the source code (found in /usr/lib/python2.7/lib-tk or the like) to find out how it's done.
Here's a quick snippet:
import tkinter
from tkinter import simpledialog
root = tkinter.Tk()
# withdraw() will make the parent window disappear.
root.withdraw()
# shows a dialogue with a string input field
youtube_url = simpledialog.askstring('YouTube URL', 'Enter the youtube URL of the video', parent=root)
if str(youtube_url).startswith('http'):
pass
else:
pass