Adding Bind Event To Entry Fields Created Using Loops - python

The main aim here is too bind events to entry fields which are created dynamically using loops and then fetch the values from those fields, how ever i am having issues here to create a function that grabs the text from the entry field as soon as the user starts typing in the box.
from tkinter import Tk, LEFT, BOTH, StringVar
from tkinter.ttk import Entry, Frame
import functools
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Entry")
self.pack(fill=BOTH, expand=1)
self.contents = []
self.ent = []
for i in range(0,5):
self.contents.append(StringVar())
# give the StringVar a default value
for i in range(0,5):
self.entry = Entry(self)
self.entry.grid(row=0,column=i)
self.entry["textvariable"] = self.contents[i]
self.entry.bind('<KeyRelease>', self.on_changed)
self.ent.append(self.entry)
def on_changed(self, event):
print('contents: {}'.format(self.contents.get()))
return True
def main():
root = Tk()
ex = Example(root)
root.geometry("800x400")
root.mainloop()
if __name__ == '__main__':
main()

When you use bind, the event object that is passed to the callback includes a reference to the widget, which you can use to get the value of the entry.
Example
def on_changed(self, event):
entry = event.widget
print('contents: {}'.format(entry.get()))
return True

Related

Creating dynamically named gui objects in Python with tkinter

I'm learning to use tkinter in Python 3.6.4. I am creating a GUI with multiple instances of buttons. Two such instances are:
def createWidgets(self):
# first button
self.QUIT = Button(self)
self.QUIT["text"] = "Quit"
self.QUIT["command"] = self.quit
self.QUIT.pack()
# second button
self.Reset = Button(self)
self.Reset["text"] = "Reset"
self.Reset["command"] = "some other function, tbd"
What I want to learn is how to abstract the instantiation of buttons such that each instance in the createWidgets method is based on a method something like this:
createButton( self, text, command, fg, bg, hgt, wth, cursor ):
What I don't know is how to control the naming of the button as:
self.QUIT
self.Reset
where the property or name following the "." operator can be passed to the createButton as a property by which the button is created and named.
Simply expanding on what Brian said, this code will get you going. The button objects are stored in a widget dictionary. Here is one way to put this together:
import tkinter as tk
import sys
root = tk.Tk()
class CustomButton(tk.Button):
def __init__(self, parent, **kwargs):
tk.Button.__init__(self, parent)
for attribute,value in kwargs.items():
try:
self[attribute] = value
except:
raise
def doReset():
print("doRest not yet implemented")
if __name__ == '__main__':
widget = {}
widget['quit'] = CustomButton(root, text='Quit', command=sys.exit)
widget['reset'] = CustomButton(root, text='Reset', command=doReset)
for button in widget.keys():
widget[button].pack()
root.mainloop()

How to create a button on a new widget using grid() method in tkinter?

import tkinter as tk
class Main:
def __init__(self, parent):
self.parent = parent
self.button = tk.Button(text="Build", command=self.new_window)
self.button.grid(row=1, column=0)
def new_window(self):
self.window = tk.Tk()
self.app = Graph(self.window)
self.window.mainloop()
class Graph:
def __init__(self, parent):
self.parent = parent
self.new_button = tk.Button(text="text")
self.new_button.grid(in_=self.parent)
def main():
root = tk.Tk()
app = Main(root)
root.mainloop()
if __name__ == "__main__":
main()
Here's my code and I'm trying to create a button on a new widget using grid() + in_, but there is a problem - button dont creates on a new widget instead of this it creates on the main one.
In tkinter widgets are by default assigned to root window, Tk unless passed a parent widget as the first positional argument, which is omitted in:
self.new_button = tk.Button(text="Destroy", command=self.destroy)
You should replace it with:
self.new_button = tk.Button(parent, text="Destroy", command=self.destroy)

Python Tkinter Input Box

Good day.
I am trying to create my own input box for use in my project.
basically what i am trying to do is run my main form which will call the second. the user will provide some data on the second and when the press the ok/close button on the second for the data will be passed back to the first. similar in functionality to the inputbox.
here is what i have created, but being new to python i am not sure where i am going wrong/nor can i quick figure out when to put the return.
My Class is here
import tkinter as tk
class MainWindow():
def __init__(self, parent):
top = self.top = tk.Toplevel(parent)
self.myLabel = tk.Label(top, text='Enter a Grouping Name')
self.myLabel.pack()
self.myEntryBox = tk.Entry(top)
self.myEntryBox.focus_set()
self.myEntryBox.pack()
self.mySubmitButton = tk.Button(top, text='OK', command=self.DestWin)
self.mySubmitButton.pack()
def DestWin(self):
self.top.destroy()
The method to call it is here
abc=configurator.MainWindow(root)
Not exactly sure what you are trying to achieve, but if you are trying to get values from one window to another, below you can find an extended example based on your code.
import tkinter as tk
class MainWindow():
def __init__(self, parent):
top = self.top = tk.Toplevel(parent)
self.myLabel = tk.Label(top, text='Enter a Grouping Name')
self.myLabel.pack()
self.myEntryBox = tk.Entry(top)
self.myEntryBox.focus_set()
self.myEntryBox.pack()
self.mySubmitButton = tk.Button(top, text='OK', command=self.DestWin)
self.mySubmitButton.pack()
def DestWin(self):
# call callback function setting value in MyFrame
self.callback(self.myEntryBox.get())
self.top.destroy()
def set_callback(self, a_func):
self.callback = a_func
class MyFrame(tk.Frame):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.pack()
self.myLabel1 = tk.Label(parent, text='Click OK to enter the group name')
self.myLabel1.pack()
self.mySubmitButton1 = tk.Button(parent, text='OK', command=self.get_group_name)
self.mySubmitButton1.pack()
def get_group_name(self):
mw = MainWindow(None)
# provide callback to MainWindow so that it can return results to MyFrame
mw.set_callback(self.set_label)
def set_label(self, astr = ''):
self.myLabel1['text'] = astr
root = tk.Tk()
mf = MyFrame(root)
root.mainloop()
The screenshot:
The text from the right window, when OK is pressed, will be shown in the left window. This is achieved through callbacks. MainWindow takes a callback function, and when you press OK, it is executed. The callback is set_label from MyFrame.
Hope this helps.

Changing the colour of a Tkinter button in a function

I want to change the colour of a button when pressing a different button. The below code recreates the Attribrute Error.
Ideally the solution should be able to change all of the attributes of the button (see the attempted state change) but I didn't put this in the title because I don't know if 'attributes' is the right word.
import Tkinter
def tester():
class window(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def initialize(self):
self.grid()
button1 = Tkinter.Button(self,text=u"Button")
button1.grid(padx=5,pady=5)
button2 = Tkinter.Button(self,text=u"Change",command=self.colourer)
button2.grid(column=1,row=0,pady=5)
button3 = Tkinter.Button(self,text=u"Disabled",state='disabled')
button3.grid(column=1,row=0,pady=5)
def colourer(self):
self.button1.configure(bg='red')
# self.button1.config(bg='red') -- this gives same error
# self.button3.configure(state='normal') -- as does this
if __name__ == "__main__":
app = window(None)
app.title('Tester')
app.mainloop()
tester()
All of the ways suggested here give the same error: Changing colour of buttons in tkinter
Thanks
The root of your problem is that you're not defining self.button. You need to assign a value to that variable:
self.button = Tkinter.Button(...)
you need give self.button1 while declaring
if you see grid you gave same column name for button2 and button 3 so they overlap each other
try this
import Tkinter
def tester():
class window(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def initialize(self):
print self.grid()
self.button1 = Tkinter.Button(self,text=u"Button")
self.button1.grid(padx=5,pady=5)
self.button2 = Tkinter.Button(self,text=u"Change",command=self.colourer)
self.button2.grid(column=1,row=0,pady=5)
self.button3 = Tkinter.Button(self,text=u"Disabled",state='disabled')
self.button3.grid(column=2,row=0,pady=5)
def colourer(self):
self.button1.configure(bg='red')
# self.button1.config(bg='red') -- this gives same error
# self.button3.configure(state='normal') -- as does this
if __name__ == "__main__":
app = window(None)
app.title('Tester')
app.mainloop()
tester()

Interacting with Tkinter window during a long process

I have a basic python class that creates a window using the standard Tkinter library:
import Tkinter
class GUI(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def lock_func(self):
while 1==1:
print "blah"
def initialize(self):
self.processBtn = Tkinter.Button(self, text="Process", command=self.lock_func)
self.processBtn.pack()
app = GUI(None)
app.mainloop()
when I hit the Process button, the window doesn't respond.
I want to be able to close the program (using the x button) whene the lock_func is runing.
You could use a generator to hold the state within the loop, and use yield to relinquish control back to the main loop. Then use self.after to repeatedly call the generator's next method to simulate the effect of while True -- but doing it in a way which is friendly to Tkinter's main loop.
import Tkinter as tk
class App(object):
def __init__(self, master):
self.master = master
self.initialize()
def lock_func(self):
def step():
while True:
print("blah")
self.nextstep_id = self.master.after(1, nextstep)
yield
nextstep = step().next
self.nextstep_id = self.master.after(1, nextstep)
def stop(self):
self.master.after_cancel(self.nextstep_id)
print("stopped")
def initialize(self):
self.nextstep_id = 0
self.process_button = tk.Button(self.master, text="Process",
command=self.lock_func)
self.stop_button = tk.Button(self.master, text="Stop",
command=self.stop)
self.process_button.pack()
self.stop_button.pack(expand='yes', fill='x')
root = tk.Tk()
app = App(root)
root.mainloop()
You can use the window.update() method too keep your GUI active and functional after every time you change something on it. During the roots mainloop, this happens automatically but if you're prolonging the mainloop it's probably a good idea to do it manually your self. Put the window.update() in the loop that is taking a while. Note: window is a Tk() object
One way is to use threading:
import Tkinter
import thread
class GUI(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def lock_func(self):
while 1==1:
print "blah"
def initialize(self):
self.processBtn = Tkinter.Button(self, text="Process", command=lambda: thread.start_new_thread(self.lock_func, ()))
self.processBtn.pack()
app = GUI(None)
app.mainloop()
However, it will keep printing until you close the Python console.
To stop it, you can use another button that changes a variable:
import Tkinter
import thread
class GUI(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.shouldPrint = True
self.initialize()
def lock_func(self):
while self.shouldPrint:
print "blah"
def setShouldPrint(self, value):
self.shouldPrint = value
def initialize(self):
self.processBtn = Tkinter.Button(self, text="Process", command=lambda: thread.start_new_thread(self.lock_func, ()))
self.stopBtn = Tkinter.Button(self, text = "Stop", command = lambda: self.setShouldPrint(False))
self.processBtn.grid(row = 1)
self.stopBtn.grid(row = 2)
app = GUI(None)
app.mainloop()

Categories