I was trying to make a simple GUI based game that has a button having the text CLICK ME .Whenever the user clicks on the button the total number clicks are displayed on the button.
Here is my code
from Tkinter import *
class Application(Frame):
def __init__(self,master):
Frame.__init__(self,master)
self.grid()
self.bttn_click = 0
self.create_widget()
def create_widget(self):
self.bttn = Button(self)
self.bttn["text"] = "Total Clicks = 0"
self.bttn["command"] = self.update_count()
self.bttn.grid()
def update_count(self):
self.bttn_click += 1
self.bttn["text"] = "Total Clicks = " + str(self.bttn_click)
#main
root = Tk()
root.geometry("900x700")
root.title("Click Counter")
app = Application(root)
root.mainloop()
Please read it from the official documentation. In there, the first "Hello World" Example has almost the same code as you do.
The command self.bttn["command"] = self.update_count() assigns the return value of def update_count(self) to your button command.
If you think to know what the result of some action should be, you could always use a print statement afterwards to verify what your assignment did.
self.bttn["command"] = self.update_count()
print(self.bttn["command"])
What exactly is the issue and where does it come from?
in the code line of yours mentioned above, you are immediately calling self.update_count and not assigning the function to be called everytime the button is pressed.
Related
I actually don't know why it does this but something is wrong with root = Label(master,text =(click))
Label.pack()
but basically what this program does is simple its just a clicker game and thing is everything else works its just when im trying to add a counter to count how many clicks a user has clicked it doesnt work
from tkinter import *
import time
from tkinter import messagebox
master = Tk()
def uiPrint():
info()
print("")
print(click)
blankLine()
Label_1 = Label(text = "Double click purchases need 50 clicks!")
Label_1.pack()
click = 0
mult = 1
dcp1 = 0
def blankLine():
for i in range(20):
print("")
def purchaseDoubleClicksCommand():
global click
global mult
if click < 50:
messagebox.showinfo("showinfo", "Not enough clicks!")
elif click >= 5:
mult = mult+1
click = click - 50
messagebox.showinfo("showinfo", "Double Clicks Purchased!")
def buttonCommand():
global click
global mult
click += 1*(mult)
root = Label(master,text =(click))
Label.pack()
mainClickButton = Button(master, text="Click!", command = buttonCommand)
mainClickButton.pack()
purchaseDoubleClickButton = Button(master, text="Purchase Double Clicks", command = purchaseDoubleClicksCommand)
purchaseDoubleClickButton.pack()
master.title("Clicker!")
master.geometry("%sx%s+%s+%s" % (400,100,512,512))
mainloop()
It seems that you are calling an instance method without an active instance:
Label.pack()
It is only possible for methods that are callable from the class itself and doesn't require an active instance/object e.g. methods decorated with #classmethod and #staticmethod. Thus change it to:
root.pack()
With the instance root, the self argument would automatically be passed as first argument, which references the root object itself.
Use root.pack() instead of Label.pack()
I am trying to create a Tkinter app, where when you press a button, a new Window opens which has continually updating text about certain parts of the program. My problem is the section of code where I am trying to add the text to the screen. This is what I have written:
import tkinter as tk
import time
class TextWindow(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.textArea = tk.Text(self, height = 10, width = 30)
self.textArea.pack(side = "left", fill = "y")
bar = tk.Scrollbar(self)
bar.pack(side = "right", fill = "y")
bar.config(command = self.textArea.yview)
def output(self, value):
outputVal = str(value)
self.textArea.inser('end', "{0}\n".format(outputVal))
self.textArea.see('end')
def openWindow():
textWindow = tk.Toplevel(root)
textFrame = TextWindow(textWindow)
textFrame.pack()
value = 0.0
alive = True
while alive:
if textWindow.winfo_exists:
value = value + 0.1
textFrame.output(value)
time.sleep(0.1)
else:
alive = False
root = tk.Tk
btn = tk.Button(root, text = "Click", command = openWindow)
btn.pack()
root.mainloop()
When I comment out the while loop in the openWindow method, the window opens and closes, and reopens, no problem. However when the code is there, I never see the window when I press the button.
I tried running it through the IDLE debugger, and I am not getting any errors, and everything runs through the loop fine, however the Window still never appears. What is my problem?
The answer that Jason S gave is not a good example. You can avoid any issues with sleep by just using after() instead. Don't settle for "Kinda works".
Here is a break down of how you could accomplish what you need without having the problems associated with sleep() and tkinter.
First you are importing Tk() wrong. Don't do tk.Tk do tk.Tk()
Now lets move the entire program into a single class. This will provide us with the ability to use class attributes and make things a bit easier to work with.
Here we create a class called guiapp(tk.Frame): you can name it what you want but this is just my example. Then make sure you are passing root using guiapp(root) so we can work in this class on the tk.Tk() instance. This will be shown at the bottom of the program where the class is instantiated.
Because we have passed root to the class we can place the button that opens the Toplevel window on our self.master attribute.
UPDATE: Changed how data is sent to the Textbox in Toplevel so we can retain the information in case you want to reopen top level. per your comment.
import tkinter as tk
class guiapp(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.value = 0.0
self.alive = True
self.list_for_toplevel = [] # added list to retain values for Toplevel
btn = tk.Button(self.master, text = "Click", command = self.TextWindow)
btn.pack()
Here we add the method to define the Topelevel we are going to create.
Because everything is inside this one class we can create this Topelevel as a Toplevel of self.master. At the end of this method we call the self.timed_loop() method I added that manages the timed portion of your program. UPDATE: added a call to a new function.
def TextWindow(self):
self.textWindow = tk.Toplevel(self.master)
self.textFrame = tk.Frame(self.textWindow)
self.textFrame.pack()
self.textArea = tk.Text(self.textWindow, height = 10, width = 30)
self.textArea.pack(side = "left", fill = "y")
bar = tk.Scrollbar(self.textWindow)
bar.pack(side = "right", fill = "y")
bar.config(command = self.textArea.yview)
self.alive = True
self.add_list_first()
UPDATE: Added a new function called add_list_first(self):. This will allow us to first add any values that are stored in the list then we can call timed_loop() to continue appending the list and counting.
def add_list_first(self):
for item in self.list_for_toplevel:
self.textArea.insert('end', "{}\n".format(item))
self.textArea.see('end')
self.timed_loop()
Here we have created a method to perform the task you have in you code for the Toplevel that uses the after() function from tkinter. ever 1000 is equal to 1 second, so play with that timer if you want. The first part of after() is for the time in milliseconds and the 2nd part is the function being called. In this case it calls itself to continue the loop until either the Toplevel window self.textWindow is closed or the self.alive variable is no longer True.
UPDATE: I have added a for loop to insert the list instead of directly imputing each value. This way we can retain the data if we want to reopen the Toplevel.
def timed_loop(self):
if self.alive == True and tk.Toplevel.winfo_exists(self.textWindow):
self.master.after(1000, self.timed_loop)
self.value += 1
self.list_for_toplevel.append(self.value)
self.textArea.delete(1.0, "end-1c")
for item in self.list_for_toplevel:
self.textArea.insert('end', "{}\n".format(item))
self.textArea.see('end')
else:
self.alive = False
This is the preferred way to start your class going in tkinter. As you can see we have created root as tk.Tk() and passed root into the the class guiapp(). Also note that I assigned this instance of the class to the variable name myapp. This will allow us to interact with the class from outside of the class if you ever need to. It does not make a difference in this case but I thought I would add it just the same.
if __name__ == "__main__":
root = tk.Tk()
myapp = guiapp(root)
root.mainloop()
Here is the copy paste version for you to use.
import tkinter as tk
class guiapp(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.value = 0.0
self.alive = True
self.list_for_toplevel = []
btn = tk.Button(self.master, text = "Click", command = self.TextWindow)
btn.pack()
def TextWindow(self):
self.textWindow = tk.Toplevel(self.master)
self.textFrame = tk.Frame(self.textWindow)
self.textFrame.pack()
self.textArea = tk.Text(self.textWindow, height = 10, width = 30)
self.textArea.pack(side = "left", fill = "y")
bar = tk.Scrollbar(self.textWindow)
bar.pack(side = "right", fill = "y")
bar.config(command = self.textArea.yview)
self.alive = True
self.add_list_first()
def add_list_first(self):
for item in self.list_for_toplevel:
self.textArea.insert('end', "{}\n".format(item))
self.textArea.see('end')
self.timed_loop()
def timed_loop(self):
if self.alive == True and tk.Toplevel.winfo_exists(self.textWindow):
self.master.after(1000, self.timed_loop)
self.value += 1
self.list_for_toplevel.append(self.value)
outputVal = str(self.value)
self.textArea.insert('end', "{0}\n".format(outputVal))
self.textArea.see('end')
else:
self.alive = False
if __name__ == "__main__":
root = tk.Tk()
myapp = guiapp(root)
root.mainloop()
The problem is that you are never giving control back to the Tkinter main loop. The code gets stuck executing in the while loop, meaning Tkinter never gets to refresh the display or process any other events. You could force update by calling root.update() right before time.sleep(0.1), but this is not really optimal and the display will be unresponsive while sleeping. Depending on what you are doing, it may be good enough.
See here for additional explanation
I'm trying to get this right but so far no luck. Would appreciate it if someone can help me with it.
import tkinter
class MyGUI:
def __init__(self):
self.main_window = tkinter.Tk()
self.button1 = tkinter.Button(self.main_window,text='Average',command=self.average)
self.button1.pack()
tkinter.mainloop()
def average(self):
self.mini_window = tkinter.Tk()
self.avg_mess = tkinter.Label(self.mini_window,text='Results:')
self.avg_result_var = tkinter.StringVar()
self.avg_result_display = tkinter.Label(self.mini_window,textvariable=self.avg_result_var)
self.avg_mess.pack()
self.avg_result_display.pack()
self.button2 = tkinter.Button(self.mini_window,text='Calculate',command=self.avg_calc)
self.button2.pack()
def avg_calc(self):
self.avg_result = (100+300+80)/3
self.avg_result_var.set(self.avg_result)
gui = MyGUI()
The problem occurs when the Calculate button is clicked but the avg_result_var does not change its value. And hence the avg.result_display remains blank. I suspect there is something wrong with the function call when the button is pressed. I'm using Python 3.x. Thanks.
You're almost doing it correctly, but there are a couple of problems
First, the result never changes because you use the same numbers each time you do a calculation. The result is always the same so it appears that it is not changing.
The second problem is that you're creating two instances of Tk. Tkinter isn't designed to work like that, and it causes problems such as the one you are observing. If you need additional pop-up windows, use Toplevel rather than Tk.
Here's a modified version of your program, though I've added a random number in the computation so you can see it change each time.
import Tkinter as tkinter
import random
class MyGUI:
def __init__(self):
self.main_window = tkinter.Tk()
self.button1 = tkinter.Button(self.main_window,text='Average',command=self.average)
self.button1.pack()
tkinter.mainloop()
def average(self):
self.mini_window = tkinter.Toplevel()
self.avg_mess = tkinter.Label(self.mini_window,text='Results:')
self.avg_result_var = tkinter.StringVar()
self.avg_result_display = tkinter.Label(self.mini_window,textvariable=self.avg_result_var)
self.avg_mess.pack(fill="both")
self.avg_result_display.pack()
self.button2 = tkinter.Button(self.mini_window,text='Calculate',command=self.avg_calc)
self.button2.pack()
def avg_calc(self):
x = random.randint(100,200)
self.avg_result = (100+300+x)/3
print "result:", self.avg_result
self.avg_result_var.set(self.avg_result)
gui = MyGUI()
My problem is that I'm trying to make a math quiz in tkinter which asks for a name, and then picks a question using the randint function. The person inputs an answer, clicks submit and the app responds to whether it was right or wrong.
The problem arises in that I then want the program to after it has been submitted clear the question and answer and come up with new ones 3 times over (each time adding to a score which is then shown at the end), however I can't seem to find a way to easily do this; I've currently been trying a while loop but it isn't working.
So my question is how would I make that part of the code loop 3 times over asking a different question each time?
My code thus far:
from tkinter import*;from random import randint
class Tk_app(Frame):
def __init__(self, root):
super(Tk_app, self).__init__(root);self.grid();self.createElements()
def nameElements(self):
self.NmLbl = Label(self, text="Name:").grid(row=0)
self.Name = Entry(self);self.Name.grid(row=0, column=1);self.score = int(0)
def createElements(self):
Frame.grid_forget(self)
self.QNum = randint(1, 2)
self.QEnt = Entry(self);self.QEnt.grid(row=1, column=1)
if(self.QNum == 1):
self.QLbl = Label(self, text="What is the air speed velocity of a flying swallow?").grid(row=1)
self.a = "African or European?"
elif(self.QNum == 2):
self.QLbl = Label(self, text="What is your quest?").grid(row=1)
self.a = "To find the holy grail."
else:
self.QLbl = Label(self, text="What is your favourite colour?").grid(row=1)
self.a = "Green"
def submit(self):
FinNam = self.Name.get()
Ans = self.QEnt.get()
if(Ans == self.a):
AnsLbl = Label(self, text = "Well done you got it right, "+FinNam).grid(row=2, column=1)
self.score+=1
else:
AnsLbl = Label(self, text = "Sorry not this time, "+FinNam+" The answer was " + self.a).grid(row=2, column=1)
self.SBut = Button(self, text="submit", command=lambda:submit(self)).grid(row=2)
root = Tk();root.title("Monty Questions")
app = Tk_app.nameElements(root)
fin = int(0)
while(fin<3):
fin+=1
app2 = Tk_app.createElements(root)
root.mainloop()
You don't want to have a while loop outside of your app class. When the program is running, it should have called root.mainloop() before the user interacts with it at all, and stay that way until it is finished. The general structure of this code is not correct.
In Tkinter I would only have this outside of the class definition:
root = Tk()
root.title("Monty Questions")
app = Tk_app()
root.mainloop()
And then you set up all of your tk widgets and whatnot in init:
class Tk_app(Frame):
def __init__(self, root):
Frame.__init__(root);
self.grid();
self.createElements()
self.nameElements()
etc.
Finally, if you just define submit() as a member function of Tk_app instead of as a nested function definition like you have it, you don't need to use a lambda function to pass self. Just do:
class Tk_app():
... __init__ and other things...
def createElements(self):
... some code ...
self.SBut = Button(self, text="submit", command=self.submit ).grid(row=2)
def submit(self, Event):
... submit code ...
The Event is necessary because not only will submit be passed self, as all member functions are, it also gets passed the event that triggered its call.
This might not get you all the way but will hopefully help structure your code in a way that will allow Tkinter to work properly. Check out examples, especially this one, to see how to structure your code. Explore that site and you should get an idea of the vibe of Tkinter.
I am trying to create a light weight cross platform Message Box that contains a list of items. Ideally it has an API that allows you to pass in a message to to display, a title, and tuple of choices. When pressing OK it would return the currently selected choice. It would also be preferred that the required modules be part of the standard python distributions.
Easygui has what I am looking for called a choicebox found at http://easygui.sourceforge.net/download/version0.95/tutorial/index.html#contents_item_10.1. However the window it pops up is monstrous and it always sorts your list of choices alphabetically. Because of these 'features', easygui is not ideal.
I have also looked into bwidgets, pmw, and Tix. While trying these I have come across a few issues including: difficultly finding working examples and failures across different platforms.
My working model is using Tkinter's OptionMenu and pickle to return the data (see code samples below). While this works, it is rather annoying having to save the choice to the file system to avoid using global variables. Is there a way to return the selection upon destruction of the gui?
Any help / advice would be greatly appreciated. Note that these examples are only for reference, they may or may not run properly on your system.
State Management Module
import pickle
def store(pkl_path, data_to_store):
try:
fid = open(pkl_path, 'w')
pickle.dump(data_to_store, fid)
except:
print 'Unable to store data in ' + pkl_path
else:
fid.close()
def load(pkl_path):
try:
fid = open(pkl_path, 'r')
loaded_state = pickle.load(fid)
fid.close()
except:
loaded_state = None
else:
fid.close()
return loaded_state
Menu Module
from Tkinter import *
def Prompt_Dropdown_Ok_Cancel(title, options, pickle_file, default_selection=0):
master = Tk()
master.title(title)
var = StringVar(master)
var.set(options[default_selection]) # default value
w = OptionMenu(master, var, *options)
w.pack()
def ok():
state.store(pickle_file, var.get())
master.quit()
def cancel():
state.store(pickle_file, None)
master.quit()
button = Button(master, text="OK", command=ok)
button.pack()
b2 = Button(master, text="Cancel", command=cancel)
b2.pack()
mainloop()
Example Usage
from menu_module import *
def display_com_selection():
pkl_path = '.tmp/comm_selection'
title = 'COM Port Selection'
Prompt_Dropdown_Ok_Cancel(title,get_available_com(),pkl_path)
selection = state.load(pkl_path)
return selection
EDIT
Disregarding my concern about global variables, I tried an implementation using them to see if it was any easier. It makes things substantially easier, however my question still stands for a better way to do this.
Below is the reworked Menu Module
from Tkinter import *
Prompt_Dropdown_Ok_Cancel_Selection = None
def Prompt_Dropdown_Ok_Cancel(title, message, options, default_selection=0):
master = Tk()
master.title(title)
var = StringVar(master)
var.set(options[default_selection]) # default value
l = Label(master, text=message)
l.pack()
w = OptionMenu(master, var, *options)
w.pack(fill=BOTH, expand=1)
def ok():
global Prompt_Dropdown_Ok_Cancel_Selection
Prompt_Dropdown_Ok_Cancel_Selection = str(var.get())
master.destroy()
def cancel():
global Prompt_Dropdown_Ok_Cancel_Selection
Prompt_Dropdown_Ok_Cancel_Selection = str(var.get())
master.destroy()
button = Button(master, text="OK", command=ok)
button.pack(side=LEFT)
b2 = Button(master, text="Cancel", command=cancel)
b2.pack(side=LEFT)
mainloop()
return Prompt_Dropdown_Ok_Cancel_Selection
The normal way dialogs work is something like this:
mydialog = SomeDialogClass(...)
result = mydialog.Show()
if result == "OK":
print "you clicked OK; dialog value is", mydialog.GetValue()
else:
print "you clicked cancel"
mydialog.Destroy()
This is pseudocode, intended to be GUI toolkit agnostic (though admittedly it looks a lot like wxPython). The main idea is, you create the dialog as an object, ask the object to show itself, wait until the user is done (by virtue of clicking "OK" or "Cancel"), then asking the object for its data and then finally destroying the object (or, keep it around for re-use).
A second way to do this is to write your code such that you give the dialog a function to call in order to set the value. Something like this:
mydialog = SomeDialogClass(..., callback=self.foo)
....
def foo(self, button, result):
if button == "OK":
print "you clicked OK; result is", result
elif button == "Cancel":
print "you clicked Cancel"
This second method works well if your dialog is not modal (ie: your program continues to run while the dialog is present).