How i can access to top level widget from function, which used as command for button or menu? There is way to add params in this command function by using command=lambda: f(params), but i think it may be easier.
You could use a lambda, like you mentioned:
command=lambda: f(params)
or you could make a closure:
def make_callback(params):
def callback():
print(params)
return callback
params = 1,2,3
button = tk.Button(master, text='Boink', command=make_callback(params))
or, you could use a class and pass a bound method. The attributes of self can contain the information that you would otherwise have had to pass as parameters.
import Tkinter as tk
class SimpleApp(object):
def __init__(self, master, **kwargs):
self.master = master
self.params = (1,2,3)
self.button = tk.Button(master, text='Boink', command=self.boink)
self.button.pack()
def boink(self):
print(self.params)
root = tk.Tk()
app = SimpleApp(root)
root.mainloop()
Related
I defined a GeneralFrame class that inherits from tk.LabelFrame, which contains other widgets like labels and entries:
class GeneralFrame(tk.LabelFrame):
def __init__(self, master, eCount, lCount):
super().__init__()
self.grid(padx=5, pady=5)
self.entry_widget(eCount)
self.label_widget(lCount)
def entry_widget(self, eCount):
self.e = {}
for i in range(0, eCount):
self.e[i] = tk.Entry(self, width=6)
self.e[i].grid(row=i, column=1, sticky='w')
self.e[i].delete(0, tk.END)
def label_widget(self, lCount):
self.l = {}
for i in range(0, lCount):
self.l[i] = tk.Label(self)
self.l[i].grid(row=i, column=0, sticky='w')
How can I use this class in a TopLevel window?
I've tried like this but it places the frame_save in parent window not TopLevel:
def openNewWindow():
newWindow = Toplevel(window)
newWindow.title('Saved Data')
newWindow.geometry('200x200')
frame_save = GeneralFrame(newWindow, eCount=5, lCount=5)
frame_save.configure(text='Saved data',font=("Helvetica",14,"bold"))
frame_save.grid(row=0, column=0)
labels_text = ['label1','label2','label3','label4','label5']
[frame_save.l[i].configure(text=labels_text[i]) for i in range(0,5)]
And general use in parent window:
window = tk.Tk()
window.geometry("980x500")
window.resizable(1,1)
window.title('Calculator')
class GeneralFrame(tk.LabelFrame):
[code]
frame_1 = GeneralFrame(window, eCount=5, lCount=5)
frame_2 = GeneralFrame(window, eCount=5, lCount=5)
def Frame_1():
[code]
def Frame_2():
[code]
Frame_1()
Frame_2()
window.mainloop()
You need to pass master when calling super().__init__. Otherwise, the actual frame has a default master, and the default master is the root window.
super().__init__(master)
Also, I encourage you to not call self.grid inside the __init__. The way tkinter widgets are designed, it's expected that the code that creates the widgets also calls pack, place, or grid on the widget. Otherwise your class can only ever be used in a parent that uses grid.
It works if I do this:
class GeneralFrame(tk.LabelFrame):
def __init__(self, master, eCount, lCount):
#super().__init__()
tk.LabelFrame.__init__(self,master)
This code of a minimal example is functional:
from tkinter import *
textbox =str()
def openpopup():
popupwindow = Toplevel(root)
global textbox
textbox = Text(popupwindow, height=20, width=40,font="Courier")
textbox.pack()
textbox.delete(1.0, END)
textbox.insert(1.0,"start")
Button(popupwindow, text="do it", command=changepopup).pack()
def changepopup():
global textbox
textbox.delete(1.0, END)
textbox.insert(1.0,"changed text")
root = Tk()
Button(root, text="open", command=openpopup).pack()
mainloop()
my goal is to open a popup dynamically on userinput and then have various gui elements interact.
I managed to do this using global. I've read using global variables should be avoided.
What is the recommended way of going about this? Can I avoid using globals? I am aware that this is an issue of scoping, this is how I came up with this "solution". I am not so familiar with OOP but I have a hunch this might be a solution here.
Question: Can I avoid using globals?
Yes, consider this OOP solution without any global.
Reference:
- 9.5. Inheritance
- class-and-instance-variables
- Dialog Windows
import tkinter as tk
from tkinter import tkSimpleDialog
class Popup(tkSimpleDialog.Dialog):
# def buttonbox(self):
# override if you don't want the standard buttons
def body(self, master):
self.text_content = ''
self.text = tk.Text(self)
self.text.pack()
return self.text # initial focus
def apply(self):
self.text_content = self.text.get(1.0, tk.END)
class App(tk.Tk):
def __init__(self):
super().__init__()
btn = tk.Button(self, text='Popup', command=self.on_popup)
btn.pack()
def on_popup(self):
# The widget `Popup(Dialog)`, waits to be destroyed.
popup = Popup(self, title='MyPopup')
print(popup.text_content)
if __name__ == '__main__':
App().mainloop()
The object-oriented way would be to create a class representing "popup" objects. The class' initializer method, __init__(), can create the popup's widgets as well as act as a storage area for the contents of the Text widget. This avoids needing a global variable because methods of class all has an first argument usually call self the is instance of the class.
Any data needed can be stored as attributes of self and can easily be "shared" all the methods of the class.
The other primary way to avoid global variables is by explicitly passing them as arguments to other callables — like main() does in the sample code below.
Here's an example based on the code in your question:
from tkinter import *
class Popup:
def __init__(self, parent):
popup_window = Toplevel(parent)
self.textbox = Text(popup_window, height=20, width=40, font="Courier")
self.textbox.pack()
self.textbox.insert(1.0, "start")
btn_frame = Frame(popup_window)
Button(btn_frame, text="Do it", command=self.do_it).pack(side=LEFT)
Button(btn_frame, text="Close", command=popup_window.destroy).pack(side=LEFT)
btn_frame.pack()
def do_it(self):
self.clear()
self.textbox.insert(1.0, "changed text")
def clear(self):
self.textbox.delete(1.0, END)
def main():
root = Tk()
Button(root, text="Open", command=lambda: Popup(root)).pack()
root.mainloop()
if __name__ == '__main__':
main()
The global you created, textbox is unnecessary. You can simply remove it from your program. and still get the same behavior
# textbox = str()
I hope my answer was helpful.
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()
I need to set some variables using tkinter callbacks on a tkinter Button. So I could say (inside of a class definition)
Button1 = Button(parentframe, text="set", command = self.setvar)
def setvar(self):
self.myvar = 7
Is there some way to do that with an inline (lambda) function, rather than cluttering things up with silly little callbacks?
command = lambda *args: something?
You could do this:
command = lambda: setattr(self, 'myvar', 7)
You could do something like this using Tkinter's integer variable class and a lambda function as you suspected. A variable of type IntVar has .get() and .set() methods that will allow you to, unsurprisingly, get and set its value. Take the following for example:
from Tkinter import *
class App(Frame):
def __init__(self, *args, **kwargs):
Frame.__init__(self, *args, **kwargs)
# initialise Integer Variables
self.example1 = IntVar()
self.example1.set(2)
self.text1 = Entry(root, textvariable=self.example1, state=DISABLED)
self.text1.grid(row=0, column=0)
self.button1 = Button(root, text="Double", command=lambda: self.double_value(self.example1))
self.button1.grid(row=1, column=0)
def double_value(self, var):
var.set(var.get() * 2)
root = Tk()
app = App(root)
root.mainloop()
This gives you a bit of flexibility, and could be expanded to apply the same feature to multiple buttons relating to different Entry boxes and IntVar variables.
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.