Tkinter - How to bind <Button-1> and call a function - python

I am trying to get the following code to work where event calls a function to clear an entry box. Can someone tell me what I am doing wrong. I am not too familiar with Tkinter.
import tkinter as tk
from tkinter import *
class Example(Frame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.master.title("Create Trusted Facts")
self.pack(fill=BOTH, expand=True)
frame2 = Frame(self)
frame2.pack(fill=X)
reqSumLbl = Label(frame2, text="Request Summary", width=22)
reqSumLbl.pack(side='left', padx=5, pady=5)
reqSumBox = Entry(frame2, width=100, bg="White", fg="lightgrey", borderwidth=1)
reqSumBox.insert(0, "Enter the Request Summary here")
reqSumBox.pack(fill=X, padx=50, pady=5, expand=True)
reqSumBox.bind("<Button-1>", self.clear_reqSumBox)
def clear_reqSumBox(self, reqSumBox):
reqSumBox.delete(0, END)
reqSumBox.config(fg="black")
global SummaryText
SummaryText = reqSumBox.get()
def main():
root = Tk()
root.geometry("500x550+350+50")
app = Example()
root.mainloop()
if __name__ == '__main__':
main()

reqSumBox.bind("<Button-1>", self.clear_reqSumBox)
When binding any event to a function, it automatically needs to take in a parameter called event, there are 2 ways to fix your code.
1.
reqSumBox.bind("<Button-1>", lambda event: self.clear_reqSumBox)
Make lambda function which takes in event and calls function.
2.
def reqSumBox(self, reqSumBox, event=None)
Add optional event parameter in reqSumBox function.
I personally use the first one.

First of all, why do you have two imports at the start of your Python script at they're both the same library, choose one it's incorrect.
About your question, it fails because you didn't provide the clicked object, it provided you as the first argument of the bind function the Event that happened.
I recommend you make your object a part of your current working class (Class Example), like that:
import tkinter as tk
from tkinter import *
class Example(Frame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.master.title("Create Trusted Facts")
self.pack(fill=BOTH, expand=True)
frame2 = Frame(self)
frame2.pack(fill=X)
reqSumLbl = Label(frame2, text="Request Summary", width=22)
reqSumLbl.pack(side='left', padx=5, pady=5)
# Check my changes here.
self.reqSumBox = Entry(frame2, width=100, bg="White", fg="lightgrey", borderwidth=1)
self.reqSumBox.insert(0, "Enter the Request Summary here")
self.reqSumBox.pack(fill=X, padx=50, pady=5, expand=True)
self.reqSumBox.bind("<Button-1>", self.clear_reqSumBox)
# Changed the argument name to "event".
def clear_reqSumBox(self, event):
self.reqSumBox.delete(0, END)
self.reqSumBox.config(fg="black")
def main():
root = Tk()
root.geometry("500x550+350+50")
app = Example()
root.mainloop()
if __name__ == '__main__':
main()
Check where I comment and analyze this code.

The callback of bind() requires an argument which is the event object. So modify the callback function definition as below:
def clear_reqSumBox(self, event):
# get the widget triggering this event
reqSumBox = event.widget
reqSumBox.delete(0, END)
reqSumBox.config(fg="black")
# after that reqSumBox.get() will return empty string
global SummaryText
# SummaryText = "" will have same result of below line
SummaryText = reqSumBox.get()
However the entry will be cleared whenever you click on the entry. Is it really what you want?

Related

How to run a function as a button command in tkinter from a 2nd window

Hi I am pretty new to tkinter and have being trying to create a button that opens a window then a have a button in the new window the gives a message when pressed. I ran into the problem that the only whay I could get it to recognise the function I wrote was to write it inside the function that opens the second window. I don't know if I have being searching for the wrong things but I can't find how to do this properly. Can someone help me out Here is my code
from tkinter import *
master = Tk()
master.title("frame control")
def win():
window2 = Toplevel()
def open():
stamp = Label(window2, text="Staped").pack()
lab2 = Button(window2,text = "yo ",command = open).pack()
lab1 = Button(master,text = " open a new window" , command = win).pack()
mainloop()
This is your code but with best practises:
import tkinter as tk
def create_stamp():
stamp = tk.Label(window2, text="Stamp")
stamp.pack()
def create_second_win():
global window2
window2 = tk.Toplevel(root)
lab2 = tk.Button(window2, text="Click me", command=create_stamp)
lab2.pack()
root = tk.Tk()
root.title("Frame control")
button = tk.Button(root, text="Open a new window", command=create_second_win)
button.pack()
root.mainloop()
I made window2 a global variable so that I can access it from create_stamp. Generally it is discouraged to use from ... import *. As #Matiiss said, sometimes you can have problems with global variables if you don't keep track of the variable names that you used.
If you want to avoid using global variables and want to use classes, look at this:
import tkinter as tk
class App:
def __init__(self):
self.stamps = []
self.root = tk.Tk()
self.root.title("Frame control")
self.button = tk.Button(self.root, text="Open a new window", command=self.create_second_win)
self.button.pack()
def create_stamp(self):
stamp = tk.Label(self.window2, text="Stamp")
stamp.pack()
self.stamps.append(stamp)
def create_second_win(self):
self.window2 = tk.Toplevel(self.root)
self.lab2 = tk.Button(self.window2, text="Click me", command=self.create_stamp)
self.lab2.pack()
def mainloop(self):
self.root.mainloop()
if __name__ == "__main__":
app = App()
app.mainloop()
As #Matiiss mentioned it would be more organised if you move the second window to its own class. For bigger projects it is a must but in this case you don't have to.

Tkinter class function causing an error when trying to utilize the entry widget

Please help me with this error. I am very confused as to why I cannot reference and obtain the contents of the entry. If someone can please help, it will be very greatly appreciated. If you need anymore context on what the problem is, please leave a comment.
When I run the program I get an error which says
self.file_location = file_location_entry.get()
NameError: name 'file_location_entry' is not defined
I am confused for getting this error since I have defined this variable below inside of the class in the main initializer function.
# Imports
import tkinter as tk
from tkinter import ttk
# ==================================================================================================
class GUI():
# Global variable for file location of software to scan
global file_location
# ==================================================================================================
# Button Functions for Windows
# Function used to open other window that will be used for the script based attacks
def script_attack_window(self):
pass
# Close Window Button Command
def close_window(self, window):
return window.quit
# Start Audit Button Command
def start_audit(self):
#function to start the security audit
pass
# Display Text To Textbox Command
def display_text_to_textbox(self, text):
self.display_textbox.insert(tk.END, text)
def set_variable(self):
self.file_location = self.file_location_entry.get()
self.display_text_to_textbox(self.file_location)
# Window for entering default location for auditing tool
def default_location_auditing_tool_window(self):
pass
# ==================================================================================================
# Main Window Function
# Rescource for using frames to organize the window better
# https://stackoverflow.com/questions/2261191/how-can-i-put-2-buttons-next-to-each-other
# https://www.tutorialspoint.com/python/tk_frame.htm
# https://realpython.com/python-gui-tkinter/
# https://tkdocs.com/tutorial/
# This will be used to display the main menu when called
def __init__(self):
# Initializing a new window
root = tk.Tk()
root.title("Main Menu")
# Making frames
canvas = tk.Frame()
blocked_space = tk.Frame(canvas)
# Initializing the window settings
root.attributes('-type', 'dialog')
#=======================Buttons=========================
# Button to start the autiting software
auditing_button = tk.Button(master=canvas, text="Audit", command=self.start_audit())
# Button to close the window
close_button = tk.Button(master=canvas, text="Close", command=self.close_window(root))
# Button to open script page
script_button = tk.Button(master=canvas, text="Script Attack", command=self.script_attack_window())
# Button to set the file variable
file_enter_button = tk.Button(master=canvas, text="Enter File Path", command=self.set_variable())
# Textbox for getting user input for file location
self.file_location_entry= tk.Entry(master=canvas)
# TextBox for displaying the text
self.display_textbox = tk.Text(master=canvas)
# Plotting the buttons and textboxes and entry on the grid
canvas.grid(column=0, row=0)
auditing_button.grid(row=3, column=1)
auditing_button.columnconfigure(1, weight=1)
close_button.grid(row=3, column=0)
close_button.columnconfigure(0, weight=1)
script_button.grid(row=3, column=2)
script_button.columnconfigure(2, weight=1)
file_enter_button.grid(column=0, row=0)
self.file_location_entry.grid(column=0, row=1, columnspan=3, sticky="ewn")
self.display_textbox.grid(column=3, row=1)
# Starting the window mainloop
root.mainloop()
# ==================================================================================================
# ---------------------------------------------------------------------------------------------------
x = GUI()
~ ```
I think that the problem is in the order in which you write the code.
Below I've rewritten your script in full OO mode, that's mean without the use
of the global variable and the double passage to assign to the text the value
of the entry variable and with the right sequence, first the app callbacks and
app variables, after GUI init, and finally the callbacks.
#!/usr/bin/python3
import sys
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
class App(tk.Tk):
"""Main Application start here"""
def __init__(self):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_close)
self.title("Simple App")
#never use global...
self.text = tk.StringVar()
self.init_ui()
def init_ui(self):
w = ttk.Frame(self, padding=5)
r = 0
c = 0
self.file_location_entry = ttk.Entry(w,
justify=tk.LEFT,
textvariable=self.text)
self.file_location_entry.grid(row=r, column=c, sticky=tk.W+tk.E, padx=5, pady=5)
r += 1
self.display_textbox = tk.Text(w)
self.display_textbox.grid(row=r, column=c, padx=5, pady=5)
r = 0
c = 1
b = ttk.LabelFrame(self, text="", relief=tk.GROOVE, padding=5)
bts = [("Audit", 1, self.start_audit, "<Alt-a>"),
("Enter File Path", 0, self.set_variable, "<Alt-e>"),
("Script Attack", 0, self.script_attack_window, "<Alt-s>"),
("Close", 0, self.on_close, "<Alt-c>")]
for btn in bts:
ttk.Button(b, text=btn[0], underline=btn[1], command = btn[2]).grid(row=r,
column=c,
sticky=tk.N+tk.W+tk.E,
padx=5, pady=5)
self.bind(btn[3], btn[2])
r += 1
b.grid(row=0, column=1, sticky=tk.N+tk.S)
w.grid(row=0, column=0, sticky=tk.N+tk.W+tk.S+tk.E)
def start_audit(self, evt=None):
pass
def script_attack_window(self, evt=None):
pass
def set_variable(self, evt=None):
self.display_textbox.insert(tk.END, self.text.get())
def on_close(self,evt=None):
"""Close all"""
if messagebox.askokcancel(self.title(), "Do you want to quit?", parent=self):
self.destroy()
def main():
app = App()
app.mainloop()
if __name__ == '__main__':
main()

Display the imported text generated from a Tk button function in a Tk Text

i'm just new to programming in python.
I'd like to create a sample project wherein I import a text file in a Button click command and display it in an Text widget. The Button function is in a different class called ButtonAction, and the widget is created in another class called Window. The problem is I don't have an idea how to get the existing Text widget so i can append the imported data.
I removed some formatting codes.
Code:
from tkinter.filedialog import *
class Window(Frame):
def __init__(self):
super().__init__()
self.initgui()
def initgui(self):
self.pack(fill=BOTH, expand=True)
textarea = Text(self)
textarea.grid(row=0, column=0, columnspan=2, rowspan=4,
padx=10, pady=10, sticky=E + W + S + N)
imprtbutton = Button(self, text="Import XML", command=ButtonAction().import_onclick)
imprtbutton.grid(row=0, column=2, padx=5, sticky=S)
class ButtonAction:
def __init__(self):
pass
def import_onclick(self):
file = askopenfile(mode='r')
if file is not None:
content = file.read()
print(content) #just so i can see if i successfully imported my txt file
if __name__ == '__main__':
root = Tk()
root.geometry("500x500+300+200")
root.title("Test Project")
gui = Window()
root.mainloop()
Hello and welcome to Stackoverflow.
When I started your script, I was not able to see the "Import XML" button at all. I needed to resize the window first. That is the reason, why I played around with the .pack and .grid calls in your code.
To solve your problem, I added the argument callback to your import_onclick method. This provides a means for the ButtonAction class to return the data back to the Window class. Here the new method update_text is responsible for filling the content of textarea.
Because we now need an additional argument for import_onclick, I wrapped the former ButtonAction().import_onclick argument into a lambda:
lambda: ButtonAction().import_onclick(self.update_text)
The resulting code works fine for me:
from tkinter.filedialog import *
class Window(Frame):
def __init__(self):
super().__init__()
self.textarea = Text(self)
self.initgui()
def initgui(self):
self.pack(fill=BOTH, expand=True)
self.textarea.pack(side=LEFT, padx=5, pady=5)
import_button = Button(self,
text="Import XML",
command=lambda: ButtonAction().import_onclick(self.update_text)
)
import_button.pack(side=RIGHT, padx=5, pady=5)
def update_text(self, text):
self.textarea.delete(1.0, END)
self.textarea.insert(END, text)
class ButtonAction:
def __init__(self):
pass
def import_onclick(self, callback):
file = askopenfile(mode='r')
if file is not None:
content = file.read()
# just so i can see if i successfully imported my txt file
print(f"read {len(content)} lines")
callback(content)
if __name__ == '__main__':
root = Tk()
root.title("Test Project")
gui = Window()
root.mainloop()

Using Python and tkinter, what benefit is there to using __init__ when creating a Frame in a class?

I am learning Python and tkinter by going through several tutorials (python-course.eu & effbot.org). I have come across a pair of situations and am trying to learn the reasons behind the differences.
NOTE: I found many questions regarding __init__ in class, but not __init__ in frame.
Both call a class from the main, and use a constructor (def __init__) to create an instance of the class. Am I using correct terminology?
They both create a frame; however, one uses Frame.__init__(self,parent): and the other uses Frame(master):
Why does the first include __init__ in the construction of the frame?
Why does the second omit 'self' in the arguments when creating the frame?
Why does the first not use 'pack' to place, but the second does?
What other differences are noteworthy?
The following are MWE to produce the respective outputs.
import tkinter as tk
class Checkbar(tk.Frame):
def __init__(self, parent=None, picks=[], side=tk.LEFT, anchor=tk.W):
tk.Frame.__init__(self, parent)
self.vars = []
for pick in picks:
var = tk.IntVar()
chk = tk.Checkbutton(self, text=pick, variable=var)
chk.pack(side=side, anchor=anchor, expand=tk.YES)
self.vars.append(var)
def state(self):
return map((lambda var: var.get()), self.vars)
if __name__ == '__main__':
root = tk.Tk()
lng = Checkbar(root, ['Python', 'Ruby', 'Perl', 'C++'])
tgl = Checkbar(root, ['English','German'])
lng.pack(side=tk.TOP, fill=tk.X)
tgl.pack(side=tk.LEFT)
lng.config(relief=tk.GROOVE, bd=2)
def allstates():
print(list(lng.state()), list(tgl.state()))
tk.Button(root, text='Quit', command=root.destroy).pack(side=tk.RIGHT)
tk.Button(root, text='Peek', command=allstates).pack(side=tk.RIGHT)
root.mainloop()
print("You've selected ", list(lng.state()))
Language Widget
import tkinter as tk
class App:
def __init__(self, master):
frame = tk.Frame(master)
frame.pack()
self.button = tk.Button(frame, text="Quit", fg="red", command=master.destroy, padx=20)
self.button.pack(side='left', expand=10)
self.hi_there = tk.Button(frame, text="Hello", command=self.say_hi)
self.hi_there.pack(side='left')
def say_hi(self):
print("Hi there, everyone!")
if __name__ == '__main__':
root = tk.Tk()
app = App(root)
root.mainloop()
Hello Window
Thank you, kindly.

Python: Tkinter OK button callback function

I'm working on my very first Python GUI and I'm trying to modify this tkinter example, but I simply cannot figure out how to write a callback function for the OK button that will pass on the entered value to the main program.
#!/usr/bin/python
# -*- coding: utf-8 -*-
from Tkinter import Tk, BOTH, StringVar, IntVar
from ttk import Frame, Button, Style, Label, Entry
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Get Value")
self.style = Style()
self.style.theme_use("default")
self.pack(fill=BOTH, expand=1)
valueLabel = Label(self, text="Value: ")
valueLabel.place(x=10, y=10)
value=StringVar(None)
value.set("this is the default value")
valueEntry=Entry(self, textvariable=value)
valueEntry.place(x=70, y=10)
quitButton = Button(self, text="Quit", command=self.quit)
quitButton.place(x=10, y=50)
okButton = Button(self, text="OK", command=self.quit)
okButton.place(x=120, y=50)
def main():
root = Tk()
root.geometry("220x100+300+300")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
I've read a gazillion of tutorials, but none of them explains this clearly. Theoretically, I should be able to get the selected value with value.get(), but I keep getting error messages no matter where I put it. Also, AFAIK, I should be able to define a default value with value.set(), but this doesn't seem to have an effect, since the text box is empty when I run the program.
What is the easiest way to pass on values to the main python program after root.mainloop() terminates? (The actual dialog box contains several entry boxes for entering string and integer values.)
I.e. I want to be able to use something like:
root = Tk()
root.geometry("220x100+300+300")
app = Example(root)
root.mainloop()
print value
print value2
print value3
How do I define default values for entry boxes?
Change every occurrence of the value variable with self.value. This should fix it and the default value will be displayed.
UPDATE
from Tkinter import Tk, BOTH, StringVar, IntVar
from ttk import Frame, Button, Style, Label, Entry
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def showMe(self):
print(self.value.get())
def initUI(self):
self.parent.title("Get Value")
self.style = Style()
self.style.theme_use("default")
self.pack(fill=BOTH, expand=1)
valueLabel = Label(self, text="Value: ")
valueLabel.place(x=10, y=10)
self.value=StringVar(None)
self.value.set("this is the default value")
valueEntry=Entry(self, textvariable=self.value)
valueEntry.place(x=70, y=10)
quitButton = Button(self, text="Quit", command=self.quit)
quitButton.place(x=10, y=50)
okButton = Button(self, text="OK", command=self.showMe)
okButton.place(x=120, y=50)
def main():
root = Tk()
root.geometry("220x100+300+300")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
Both your quitButton and okButton call the self.quit functions. So no mater what value you enter when you press the OK button you are calling the quit function which has its own problems as well outside the scope of your question.
Try to define value as self.value and make the okButton call a function that does: print self.value.get().

Categories

Resources