I have a class with a button "Save game", which opens a popup with textinput widget and a button with which I would like to save the users text input. I tried this but it doesn't work.
If I print out the filename after "modifying" it, it prints out the filename that I defined beforehand: .txt, not Game1.txt or something like this.
class Something(Widget):
filename = ".txt"
# code for game
def save(self,*args):
def on_text(instance, value):
return value
name = TextInput(text="")
name.bind(text=on_text)
self.filename = "{}.txt".format(name.text)
b = BoxLayout()
save_button = Button(text="Save file")
b.add_widget(save_button)
b.add_widget(name)
popup = Popup(title="Enter the name:", content=b)
save_button.bind(on_release=popup.dismiss)
name.bind(on_text_validate=popup.on_dismiss) # I wanted to save it when the user closes the popup
popup.open()
Where am I doing it wrong? (I know this won't actually save it, I just want to create the name here, I have a different class method for saving it)
I'm looking at a similar problem: when the user types text into a text input box, but does not hit return before hitting the 'exit' button, the changes are lost.
You might want to see if this is your error as well.
Related
i have a folderscreen that displays buttons from a loop, the buttons have title of the files saved, when i click the buttons, it opens a new page that shows the content of the file, but when i go back to the folder screen, 2 sets of the button in the loop are added to the existing buttons.
what i want is everytime i leave the folderscreen, i want the buttons cleared, so that when i go back, it will run the loop code again and shows only the buttons from the loop without repetition.
or may be there is a way i can stop the loop code from running if the boxlayout that house the buttons is not empty. here is my code:
def on_enter(self, *args):
objf = mm.Storenote()
objf.tif(mm.DB.hod)
for i in range(len(mm.Storenote.ht)):
self.b = Button(text= (mm.Storenote.ht[i]), font_size = "25sp")
#self.b.background_normal = ""
self.b.background_color = 0,0,1,1
self.b.ids = {"id":mm.Storenote.nid[i]}
self.b.size_hint=(1, None)
self.b.size = (dp(370), dp(50))
self.b.bind(on_press=self.build_clickk(self.b))
self.ids.lpl.add_widget(self.b)
#self.ids.lpl.add_widget(self.l[i])
def build_clickk(self, b):
def clickk(*args):
ci = b.ids.id
print(ci)
objff = mm.Show()
objff.getter(ci)
self.manager.current = "Readpage"
return clickk
def on_leave(self, *args):
self.ids.lpl.remove_widget(self.b)
the def on_leave function only remove one of the buttons and add 2 new sets of the button each time i go back to the folderscreen
You can use self.ids.lpl.clear_widgets() right before your for-loop. This way you ensure that the layout is empty before adding new widgets.
If you add many widgets, try using def on_pre_enter(). kivy will render the widgets before entering the screen and this may prevent "flickering" when building all widgets.
I'm trying to create a small GUI application where both the user and the computer are clicking the button widget turn by turn.
First, the user Click the Button and the Button text changes, then the computer clicks the button and the different texts appear on the button for the machine
Here's the code.
from tkinter import *
globals()['status'] = True
class test11(Tk):
def __init__(self):
super().__init__()
self.geometry('200x200')
self.btn_1 = Button(self,text="CLICK ME",command=lambda :self.test_function(1,2))
self.btn_1.grid()
if not (globals()['status']):
self.machine()
def test_function(self,aa,bb):
self.btn_1['text'] = f'Dont CLICK ME {aa} {bb} HUMAN'
globals()['status'] = False
def machine(self):
self.btn_1['text'] = f'Computer just clicked me'
globals()['status'] = True
if __name__ == '__main__':
a = test11()
a.mainloop()
But the problem is the turn of the machine never comes even though the user has already pressed the button.
As the user presses the button the values of global variable changes which should have let the machine function to call isn't it?
I have an Entry that updates once via a textvariable set to a StringVar, but then only via manual keypress from user after that.
I'm writing a GUI using model-view-controller pattern.
I have a controller class, that owns the main tk object.
It also owns a StringVar(). I've set up a callback to fire when the StringVar is written to.
from Tkinter import *
from tkFileDialog import askopenfilename
class Controller:
def __init__(self):
self.root = Tk()
self.fname = StringVar()
self.fname.trace('w', self.file_name_updated)
self.view = View(self.root, self.fname)
def file_name_updated(self, name, index, mode):
varValue = self.root.globalgetvar(name)
print ('filename updated!!! %s' % varValue)
self.root.globalsetvar(name, 'changing it again!')
def run(self):
self.root.mainloop()
class View:
def __init__(self, tk, fname_var):
self.form = tk
self.fileNameVar = fname_var
self.inFileTxt = Entry(self.form, textvariable=fname_var)
self.inFileTxt.grid(row=1)
self.inFileBtn = Button(self.form, text="Browse ...", command=self.open_file)
self.inFileBtn.grid(row=1, column=8)
def open_file(self):
fname = askopenfilename()
self.fileNameVar.set(fname) # update the updateable text
if __name__ == '__main__':
c = Controller()
c.run()
The controller creates a View instance. The View object, draws an Entry box which assigns the StringVar to its textvariable attribute.
The View object also has a Button. When clicked, this sets the value of the StringVar(), and fires the callback.
So far so good. The StringVar has been set to the chosen filename, the callback fires, I see my debug. But then, in my callback, I update the value of the StringVar again. It's just a test, ultimately I want to clear the value of the StringVar if the filename chosen has errors (but that wasn't working, so I'm trying to narrow it down).
Anyway, I'm expecting the text in the entry box to say 'changing it again!'. But I don't see that. It stays the same.
Debug shows that the value of the variable is 'changing it again!', but the Entry box has not updated.
However, when I place the cursor inside the Entry box and press SPACE, then my text gets updated.
How can I update the text without user having to enter anything?
possibly related - event processing, StringVar is always 1 event behind.
Tkinter: set StringVar after <Key> event, including the key pressed
When the insert method of the Entry is used instead of the set method of the StringVar in the open_file function, it works as expected (the text is replaced by "changing it again!"):
def open_file(self):
fname = askopenfilename()
self.inFileTxt.delete(0, 'end')
self.inFileTxt.insert(0, fname)
instead of
def open_file(self):
fname = askopenfilename()
self.fileNameVar.set(fname) # update the updateable text
But I don't understand why because in both cases file_name_updated is executed. So if someone can explain this, please edit this answer.
I have created a simple command-line game and am considering porting it to GUI. This way I would be able to give the player the option to make choices through clicking buttons, instead of being forced to type in text.
My problem is that it would be tricky to do without the ability to change the text on Label and Button widgets, so how might one go about properly doing this?
Here is what I have so far (after laurencevs's answer):
def goAway(event):
label02.configure(text = " ")
label01.configure(text = "Go away")
time.sleep(1)
label01.configure(text = "GO AWAY.")
time.sleep(1)
label01.configure(text = "Seriously, go AWAY!")
time.sleep(1)
label01.configure(text = "That's it.")
time.sleep(0.5)
quit("GOODBYE.")
button01 = Button(root, text="Click me, see what happens.")
button01.grid(row=1001, column=1001)
button01.bind("<Button-1>", goAway)
But all it does is wait 3 seconds and then close the program. How can I fix this
The idea is that when clicked the button will change the text in the Label label01 to "Go away", wait one second, change the text to "GO AWAY.", etc. and then quit, printing "GOODBYE" to users running it in a terminal.
You absolutely can change the text on a Label or a Button.
All you have to do is use the Label.configure() method. Say you want to change the text in label1 to "Don't Panic", you just do this:
label1.configure(text = "Don't Panic")
The same goes for buttons and other widgets.
If you want to create a Button that does this when it is clicked, you must define a function which changes the label's text, and then use the function's name (for example foo) when creating the Button like this:
button = Button(window, text = "I am a button", command = foo)
The full code for that would look like this:
from tkinter import * # Tkinter in Python 2
def foo():
label1.configure(text = "Don't Panic")
window = Tk()
# other (optional) window setup here
label1 = Label(window, text = "")
button = Button(window, text = "I am a button", command = foo)
# pack the label and button and initiate the window's mainloop here
When opening a new tkinter window, I only want the user to be able to click buttons on the new window. They should not be able to click on buttons from other windows that are part of the application. How would I accomplish this?
Here is a snip of my code:
def exportEFS(self):
self.exportGUI = Toplevel()
Button(self.exportGUI, text='Backup', command=self.backup).pack(padx=100,pady=5)
Button(self.exportGUI, text='Restore', command=self.restore).pack(padx=100,pady=5)
def backup(self):
self.backupWindow = Toplevel()
message = "Enter a name for your Backup."
Label(self.backupWindow, text=message).pack()
self.entry = Entry(self.backupWindow,text="enter your choice")
self.entry.pack(side=TOP,padx=10,pady=12)
self.button = Button(self.backupWindow, text="Backup",command=self.backupCallBack)
self.button.pack(side=BOTTOM,padx=10,pady=10)
In this snip, once the backupWindow is opened, the exportGUI remains open, but the user should not be able to click "Backup" or "Restore" while the backupWindow is opened.
Thanks!
You will want to call grab_set on the TopLevel window so that all keyboard and mouse events are sent to that.
def exportEFS(self):
self.exportGUI = Toplevel()
Button(self.exportGUI, text='Backup', command=self.backup).pack(padx=100,pady=5)
Button(self.exportGUI, text='Restore', command=self.restore).pack(padx=100,pady=5)
def backup(self):
self.backupWindow = Toplevel()
self.backupWindow.grab_set()
message = "Enter a name for your Backup."
Label(self.backupWindow, text=message).pack()
self.entry = Entry(self.backupWindow,text="enter your choice")
self.entry.pack(side=TOP,padx=10,pady=12)
self.button = Button(self.backupWindow, text="Backup",command=self.backupCallBack)
self.button.pack(side=BOTTOM,padx=10,pady=10)
What you can do is set the state to disabled. As so:
self.button.config(state="disabled")
And to enable it, you just use:
self.button.config(state="normal")
However, you must assign your buttons to variables first, like this:
self.backup=Button(self.exportGUI, text='Backup', command=self.backup)
self.backup.pack(padx=100,pady=5)
self.restore=Button(self.exportGUI, text='Restore', command=self.restore)
self.restore.pack(padx=100,pady=5)
so you would disable these using:
self.backup.config(state="disabled")
self.restore.config(state="disabled")
and re-enable using:
self.backup.config(state="normal")
self.restore.config(state="normal")
Please note however, that while the button is disabled, nothing can be changed to that button, both through the code, or through the user using it. So that means if you wanted to change the text of that button, you would have to change the state of the button to "normal" before changing it (if it already isn't in that state, which by default, all widgets are in that state when first created).
Cheers :)