Tkinter: Calling function when button is pressed - python

import tkinter as tk
def load(event):
file = open(textField.GetValue())
txt.SetValue(file.read())
file.close()
def save(event):
file = open(textField.GetValue(), 'w')
file.write(txt.GetValue())
file.close()
win = tk.Tk()
win.title('Text Editor')
win.geometry('500x500')
# create text field
textField = tk.Entry(win, width = 50)
textField.pack(fill = tk.NONE, side = tk.TOP)
# create button to open file
openBtn = tk.Button(win, text = 'Open', command = load())
openBtn.pack(expand = tk.FALSE, fill = tk.X, side = tk.TOP)
# create button to save file
saveBtn = tk.Button(win, text = 'Save', command = save())
saveBtn.pack(expand = tk.FALSE, fill = tk.X, side = tk.TOP)
I get the error that load and save are missing a position argument: event. I understand the error, but don't understand how to resolve it.

Here's a runnable answer. In addition to changing the commmand= keyword argument so it doesn't call the function when the tk.Buttons are created, I also removed the event argument from the corresponding function definitions since tkinter doesn't pass any arguments to widget command functions (and your function don't need it, anyway).
You seem to be confusing event handlers with widget command function handlers. The former do have an event argument passed to them when they're called, but the latter typically don't (unless you do additional stuff to make it happen—it's a fairly common thing to need/want to do and is sometimes referred to as theThe extra arguments trick).
import tkinter as tk
# added only to define required global variable "txt"
class Txt(object):
def SetValue(data): pass
def GetValue(): pass
txt = Txt()
####
def load():
with open(textField.get()) as file:
txt.SetValue(file.read())
def save():
with open(textField.get(), 'w') as file:
file.write(txt.GetValue())
win = tk.Tk()
win.title('Text Editor')
win.geometry('500x500')
# create text field
textField = tk.Entry(win, width=50)
textField.pack(fill=tk.NONE, side=tk.TOP)
# create button to open file
openBtn = tk.Button(win, text='Open', command=load)
openBtn.pack(expand=tk.FALSE, fill=tk.X, side=tk.TOP)
# create button to save file
saveBtn = tk.Button(win, text='Save', command=save)
saveBtn.pack(expand=tk.FALSE, fill=tk.X, side=tk.TOP)
win.mainloop()

Remove the 'event' arguments from both function defs and remove brackets from your commands.

Use button.bind() mehtod and pass 'Button-1' in first argument and function name in second argument
def functionname(event):
#your code
button = tk.Button(win, text="Login", bg="green")#, command=attempt_log_in)
button.grid(row=5, column=2)
button.bind('<Button-1>', functionname)

You have functions save and load that takes 1 argument : event but when you called
# create button to open file
openBtn = tk.Button(win, text = 'Open', command = **load()**) <<<--- no argument in load
<<<---should be load(someEvent)
openBtn.pack(expand = tk.FALSE, fill = tk.X, side = tk.TOP)
# create button to save file
saveBtn = tk.Button(win, text = 'Save', command = **save()**)<<< ---- same for the save
saveBtn.pack(expand = tk.FALSE, fill = tk.X, side = tk.TOP)
But in your function save and load the argument : event you don't do nothing with it so you can just delete the event argument and this problem shouldn't appear. Like this :
def load():
file = open(textField.GetValue())
txt.SetValue(file.read())
file.close()
def save():
file = open(textField.GetValue(), 'w')
file.write(txt.GetValue())
file.close()

Related

display not changing despite variables being changed on tkinter

I want to write a program where after a user enters text and clicks a button, the text becomes a label and the button text is changed. My code is:
# Imports
import os, sys
import tkinter
"""
Tkinter program 1
text box + button + label
"""
# Button Entry
def enter(inputtedinfo, randvar, EnterMessage):
randvar = inputtedinfo.get()
EnterMessage = "Submitted!"
# Main Function
def main():
something = tkinter.Tk()
something.title("My First Tkinter Window")
something.geometry("600x400")
randvar = ""
EnterMessage = "Enter"
inputtedinfo = tkinter.StringVar()
userLabel = tkinter.Label(something, text = randvar)
userEntry = tkinter.Entry(something, textvariable = inputtedinfo)
userButton = tkinter.Button(something, text = EnterMessage, command = enter(inputtedinfo, randvar, EnterMessage))
userEntry.grid(row=0,column=0)
userLabel.grid(row=0,column=1)
userButton.grid(row=0,column=2)
something.mainloop()
sys.exit(0)
if(__name__ == "__main__"):
main()
The user input works, but clicking the button does nothing despite the fact that it is supposed to change the variables for the button and label displays. Did I mess up somewhere?
The command argument takes the name of a function. If you write the complete call with arguments, it's not the name of the function but whatever is returned by this exact function call. So, your button will not work. It will have the command None.
In order to do what you want to do, you have to make the StringVar()s accessible to the function you are calling. So, you can both get the contents of the entry and change the values of the button and the label. To do this, best add the string variables and the widgets as attributes to the toplevel you already created (something). So, they stay available to all functions and you can get and change information:
# Button Entry
def enter():
something.randvar.set(something.inputtedinfo.get())
something.userButton["text"] = "Submitted!"
# Main Function
def main():
global something
something = tkinter.Tk()
something.title("My First Tkinter Window")
something.geometry("600x400")
something.randvar = tkinter.StringVar()
something.randvar.set("")
EnterMessage = "Enter"
something.inputtedinfo = tkinter.StringVar()
userLabel = tkinter.Label(something, textvariable = something.randvar)
something.userEntry = tkinter.Entry(something, textvariable = something.inputtedinfo)
something.userButton = tkinter.Button(something, text = EnterMessage, command = enter)
something.userEntry.grid(row=0,column=0)
userLabel.grid(row=0,column=1)
something.userButton.grid(row=0,column=2)
something.mainloop()
if(__name__ == "__main__"):
main()
There are few issues in your code:
assign string to textvariable, should use StringVar instead
command=enter(...) will execute enter(...) immediately and then assign None to command option, should use lambda instead
updating strings inside enter() does not automatically update the label and the button, should use .set() on the StirngVar instead
Below is modified code:
def enter(inputtedinfo, randvar, EnterMessage):
# used .set() to update StringVar
randvar.set(inputtedinfo.get())
EnterMessage.set("Submitted!")
def main():
something = tkinter.Tk()
something.title("My First Tkinter Window")
something.geometry("600x400")
randvar = tkinter.StringVar() # changed to StringVar()
EnterMessage = tkinter.StringVar(value="Enter") # changed to StringVar()
inputtedinfo = tkinter.StringVar()
userLabel = tkinter.Label(something, textvariable=randvar) # used textvariable instead of text option
userEntry = tkinter.Entry(something, textvariable=inputtedinfo)
userButton = tkinter.Button(something, textvariable=EnterMessage, command=lambda: enter(inputtedinfo, randvar, EnterMessage))
userEntry.grid(row=0,column=0)
userLabel.grid(row=0,column=1)
userButton.grid(row=0,column=2)
something.mainloop()

call back not being called in tkinter bind

Here's my problem, when keyboard focus is on KeyboardEntry, KeysPressed() should happen, when focus leaves, toggle() should happen.
KeysPressed has a while loop such as:
while Flag:
#Whatever
toggle just toggles the flag
Flag= not Flag
The idea is when focus leaves KeyComboEntry, KeysPressed() stops running
This does not happen, at all matter of fact, toggle is not called
my
# Program by Fares Al Ghazy started 20/5/2017
# Python script to assign key combinations to bash commands, should run in the background at startup
# this program is meant to release bash code, it is obviously non-system agnostic and only works linux systems that use BASH
# is one file which only creates the GUI, another file is needed to use the info taken by this program
FileName = 'BinderData.txt'
import tkinter as tk
from ComboDetect import ComboDetector
from _thread import start_new_thread
# Create a class to get pressed keys and print them
KeyManager = ComboDetector()
# Class that creates GUI and takes info to save in file
class MainFrame(tk.Tk):
# variable to store pressed keys
KeyCombination = ""
testcounter = 1
Flag = True
#function to toggle Flag
def Toggle(self):
print("toggle")
self.Flag = not self.Flag
# function to write to file
def SaveFunction(self, e1, e2, FileName):
file = open(FileName, "a")
combo = e1.get() + '\n'
performed = e2.get() + '\n'
file.write(combo)
file.write(performed)
file.close()
def KeysPressed(self, Entry, KeyCombination):
Entry.config(state="normal")
#Entry.insert(tk.END, "Test")
while self.Flag:
print("test "+str(self.testcounter))
self.testcounter = self.testcounter + 1
KeyCombination = str(KeyManager.getpressedkeys())
Entry.delete(0, tk.END)
Entry.insert(tk.END, KeyCombination)
# constructor
def __init__(self, FileName, **kwargs):
tk.Tk.__init__(self, **kwargs)
# create GUI to take in key combinations and bash codes, then save them in file
root = self # create new window
root.wm_title("Key binder") # set title
# create labels and text boxes
KeyComboLabel = tk.Label(root, text="Key combination = ")
KeyComboEntry = tk.Entry(root)
# Bind function to entry
KeyComboEntry.bind('<FocusIn>', lambda e: start_new_thread(self.KeysPressed, (KeyComboEntry, self.KeyCombination)))
KeyComboEntry.bind('<FocusOut>', lambda f:self.toggle, ())
ActionLabel = tk.Label(root, text="Command to be executed = ")
ActionEntry = tk.Entry(root)
# place widgets in positions
KeyComboLabel.grid(row=0, column=0, sticky=tk.E)
ActionLabel.grid(row=1, column=0, sticky=tk.E)
KeyComboEntry.grid(row=0, column=1)
ActionEntry.grid(row=1, column=1)
# create save button
SaveButton = tk.Button(root, text="save",
command=lambda: self.SaveFunction(KeyComboEntry, ActionEntry, FileName))
SaveButton.grid(row=2, column=2, sticky=tk.E)
app = MainFrame(FileName)
app.mainloop()

delete buttons defined in other function

I'm trying to get a button to dissapear when you click it, and other buttons to appear. Then when the "back" button is clicked, I want the newer buttons to dissapear again, and the original button to appear again.
The problem is that I don't know how to make a function retrieve information from another function. If I try to do anything to the search_button in the search(event) function, the search_button is not defined because it was only defined in the main() function.
import tkinter as tk
window = tk.Tk()
def search(event):
#insert "delete search_button" logic here
easy_button = tk.Button(window, text = "Easy")
easy_button.bind("<Button-1>", easy_search)
easy_button.pack()
back_button = tk.Button(window, text = "Back")
back_button.bind("<Button-1>", back_button1) #had to put 1 on end here. It seems back_button is predefined as an object
back_button.pack()
def easy_search(event):
#does a bunch of stuff that doesn't matter for this question
pass
def back_button1(event):
#this should delete easy_button and reinitiate search_button
pass
def main():
search_button = tk.Button(window, text = "Search")
search_button.bind("<Button-1>", search)
search_button.pack()
main()
window.mainloop()
The easiest way is to make everything into a class, in which all your functions could share the same self namespace. And that if you want to bind the button pressed with another function, use 'command' instead, unless you're actually using the event.
That will come together to this:
import tkinter as tk
window = tk.Tk()
class Search:
def __init__(self):
self.search_button = tk.Button(window, text = "Search")
self.search_button['command'] = self.search
self.search_button.pack()
def search(self):
self.search_button.pack_forget() # or .destroy() if you're never going to use it again
self.easy_button = tk.Button(window, text = "Easy")
self.easy_button['command'] = self.easy_search
self.easy_button.pack()
self.back_button = tk.Button(window, text = "Back")
self.back_button['command'] = self.back_button1
self.back_button.pack()
def easy_search(self):
#does a bunch of stuff that doesn't matter for this question
pass
def back_button1(self):
#this should delete easy_button and reinitiate search_button
pass
widgets = Search()
window.mainloop()
There you can call the widget's destroy or pack_forget command.

Python Tkinter Toplevel runs without calling

Hello I have some very simple python code:
from Tkinter import *
from tkFileDialog import askopenfilename
def createnewguiprojectgui():
asktk = Toplevel()
asktk.title("Create new gui source")
Label(asktk, text="Gui options").pack(side=TOP)
askfilename = Entry(asktk)
askfilename.insert(0, "Source name")
askfilename.pack(side=LEFT,fill=X)
yesfornew = Button(asktk, text="cancel", command=createnewguiproject())
nofornew = Button(asktk, text="cancel",command=) #a command
def createnewguiproject():
pass
def main():
mainparent = Tk()
w, h = mainparent.winfo_screenwidth(), mainparent.winfo_screenheight()
mainparent.title("GUI CREATOR")
mainmenu = Menu(mainparent)
filemenu = Menu(mainmenu,tearoff = 0)
filemenu.add_command(label="New project", command = createnewguiprojectgui())
mainmenu.add_cascade(label="File", menu=filemenu)
separator = Frame(height=2, bd=1, relief=SUNKEN)
separator.pack(fill=X, padx=5, pady=5)
mainparent.config(menu=mainmenu)
mainmenu.focus_set()
mainparent.mainloop()
if __name__ == "__main__":
main()
However, every time I run ths script, the asktk toplevel pops up with the mainparent Tk, even if I don't press the menu bar, and the focus is set to asktk. Whats wrong?
The command here is calling the function that creates a new window object onload rather than when New Project is selected.
filemenu.add_command(label="New project", command = createnewguiprojectgui())
You need to change it to:
filemenu.add_command(label="New project", command = createnewguiprojectgui)
Try this: in the line that has ...
filemenu.add_command(label="New project", command = createnewguiprojectgui())
take the () off the end of createnewguiprojectgui. In other words, pass the function, don't call it.
The same should be true for the following line ...
yesfornew = Button(asktk, text="cancel", command=createnewguiproject())
Don't call the function command=createnewguiproject(). Remove the (). Your createnewguiproject() method gets called as soon as the main loop starts.
Do this:
yesfornew = Button(asktk, text="cancel", command=createnewguiproject)
Or if you want to send some argument then:
yesfornew = Button(asktk, text="cancel", command=lambda:createnewguiproject(args))

Using the variable from entry/button in another function in Tkinter

When I press the button, I want it to get the Entry and -for future things- use it in another function.
import tkinter
def ButtonAction():
MyEntry = ent.get() #this is the variable I wanna use in another function
den = tkinter.Tk()
den.title("Widget Example")
lbl = tkinter.Label(den, text="Write Something")
ent = tkinter.Entry(den)
btn = tkinter.Button(den, text="Get That Something", command = ButtonAction )
lbl.pack()
ent.pack()
btn.pack()
den.mainloop()
print MyEntry #something like this maybe. That's for just example
I will use this thing as a search tool. Entry window will appear, get that "entry" from there and search it in files like:
if MyEntry in files:
#do smth
I know I can handle the problem with using globals but from what I've read it's not recommended as a first solution.
Structure the program using class.
import tkinter
class Prompt:
def button_action(self):
self.my_entry = self.ent.get() #this is the variable I wanna use in another function
def __init__(self, den):
self.lbl = tkinter.Label(den, text="Write Something")
self.ent = tkinter.Entry(den)
self.btn = tkinter.Button(den, text="Get That Something", command=self.button_action)
self.lbl.pack()
self.ent.pack()
self.btn.pack()
den = tkinter.Tk()
den.title("Widget Example")
prompt = Prompt(den)
den.mainloop()
You can access the input using prompt.my_entry later.

Categories