Related
I'm trying to build a script for import in my future projects.
That Script should create some tk.Frames in a tk.Frame and let me edit the created ones in a main.
I think, the best way to get there is to create a Holder_frame class and put some nested classes in.
so I could call them in my main with Holder_frame.F1.
I tried a lot of code and I ended up here making me an account.
Anyway here is where Im at:
import tkinter as tk
from tkinter import Frame,Button
class BaseClass(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.pack()
class Holder_frame(tk.Frame):
Names = []
def __init__(self, master, frames=2):
tk.Frame.__init__(self, master)
self.master = master
frame_names = Holder_frame.Names
for i in range(0,frames):
frame_names.append("F"+str(i+1))
print(frame_names)
Holder_frame.factory()
def factory():
print(Holder_frame.Names)
print(type(BaseClass))
for idex,i in enumerate (Holder_frame.Names):
print(i)
class NestedClass(BaseClass):
pass
NestedClass.__name__ = i
NestedClass.__qualname__ = i
if __name__ == "__main__":
root = tk.Tk()
def raise1():
Holder_frame.F1.tkraise()
def raise2():
Holder_frame.F2.tkraise()
holder=Holder_frame(root,frames=2)
holder.grid(row=1,column=0)
b1 = tk.Button(root, text='1', command=raise1)
b1.grid(row=0,column=0)
b2 = tk.Button(root, text='2', command=raise2)
b2.grid(row=0,column=1)
root.mainloop()
Everything works fine, till I try to call a Frame.
(AttributeError 'Holder_frame' object has no attribute 'F1')
I think my problem is the structure but need some help to solve it.
Any suggestions?
If I'm getting it right I think you mean to have some sort of a Base class that has some configuration which a set of frames have in common like for example you want to have 10 frames of 300x400 geometry and of a brown background in common and later having another set of frames with a different configuration, which can be accessed in an organised way. Then I would say you have an interesting way but I would rather use a list or a dictionary anyway.
Here are some approaches to achieve this goal.
Approach 1
In this approach, I've created a function that returns a dictionary with all the frames created and contained in it like in format ({..., 'F20': tkinter.frame, ...})
import tkinter as tk
def get_base_frames(num, master, cnf={}, **kw):
"""
Create list of frames with common configuration options.
Args:
num (int): Number of frames to be created.
master (tk.Misc): Takes tkinter widget or window as a parent for the frames.
cnf (dict): configuration options for all the frames.
kw: configuration options for all the frames.
Return:
Dictionary of frames ({..., 'F20': tkinter.frame, ...}).
"""
return {f'F{n+1}': tk.Frame(master, cnf=cnf, **kw) for n in range(num)}
if __name__ == "__main__":
root = tk.Tk()
frame_holder = get_base_frames(10, root, width=50, height=50, bg='brown')
# Frames can be accessed through their names like so.
print(frame_holder.get('F1'))
Approach 2
Here I've used class and objects. Where I made this class Frames though you can name it anything you want. I also added some important method like cget() and configure(), through these methods once get a value to an option and configure options for all the frames respectively. There are more useful methods like bind() and bind_all() if you need those just modify this class as per your need.
import tkinter as tk
class Frames(object):
def __init__(self, master=None, cnf={}, **kw):
super().__init__()
num = cnf.pop('num', kw.pop('num', 0))
for n in range(num):
self.__setattr__(f'F{n+1}', tk.Frame(master, cnf=cnf, **kw))
def configure(self, cnf={}, **kw):
"""Configure resources of a widget.
The values for resources are specified as keyword
arguments. To get an overview about
the allowed keyword arguments call the method keys.
"""
for frame in self.__dict__:
frame = self.__getattribute__(frame)
if isinstance(frame, tk.Frame):
if not cnf and not kw:
return frame.configure()
frame.configure(cnf=cnf, **kw)
config = configure
def cget(self, key):
"""Return the resource value for a KEY given as string."""
for frame in self.__dict__:
frame = self.__getattribute__(frame)
if isinstance(frame, tk.Frame):
return frame.cget(key)
__getitem__ = cget
if __name__ == "__main__":
root = tk.Tk()
frame_holder = Frames(root, num=10, width=10,
bd=2, relief='sunken', bg='yellow')
# Frames can be accessed through their naems like so.
print(frame_holder.F4)
print(frame_holder['bg'])
frame_holder.config(bg='blue')
print(frame_holder['bg'])
Approach 3
If you want to have differently configured frames contained in one class, where all those frames have some method in common or some attribute in common.
import tkinter as tk
class BaseFrame(tk.Frame):
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master=master, cnf={}, **kw)
def common_function(self):
"""This function will be common in every
frame created through this class."""
# Do something...
class FrameHolder(object):
def __init__(self, master=None, cnf={}, **kw):
kw = tk._cnfmerge((cnf, kw))
num = kw.pop('num', len(kw))
for n in range(num):
name = f'F{n+1}'
cnf = kw.get(name)
self.__setattr__(name, BaseFrame(master, cnf))
if __name__ == "__main__":
root = tk.Tk()
holder = FrameHolder(root,
F1=dict(width=30, height=40, bg='black'),
F2=dict(width=50, height=10, bg='green'),
F3=dict(width=300, height=350, bg='blue'),
F4=dict(width=100, height=100, bg='yellow'),
)
print(holder.F1)
print(holder.__dict__)
Approach 4
This is the approach that OP is trying to achieve.
import tkinter as tk
class BaseClass(tk.Frame):
def __init__(self, master, cnf={}, **kw):
kw = tk._cnfmerge((cnf, kw))
cnf = [(i, kw.pop(i, None))
for i in ('pack', 'grid', 'place') if i in kw]
tk.Frame.__init__(self, master, **kw)
self.master = master
if cnf:
self.__getattribute__(cnf[-1][0])(cnf=cnf[-1][1])
class Container(tk.Frame):
"""Container class which can contain tkinter widgets.
Geometry (pack, grid, place) configuration of widgets
can also be passed as an argument.
For Example:-
>>> Container(root, widget=tk.Button,
B5=dict(width=30, height=40, bg='black',
fg='white', pack=(), text='Button1'),
B6=dict(width=50, height=10, bg='green', text='Button2',
place=dict(relx=0.5, rely=1, anchor='s')))
"""
BaseClass = BaseClass
def __init__(self, master=None, cnf={}, **kw):
kw = tk._cnfmerge((cnf, kw))
wid = kw.pop('widget', tk.Frame)
for name, cnf in kw.items():
geo = [(i, cnf.pop(i, None))
for i in ('pack', 'grid', 'place') if i in cnf]
setattr(Container, name, wid(master, cnf))
if geo:
manager, cnf2 = geo[-1]
widget = getattr(Container, name)
getattr(widget, manager)(cnf=cnf2)
if __name__ == "__main__":
root = tk.Tk()
Container(root, widget=Container.BaseClass,
F1=dict(width=30, height=40, bg='black', relief='sunken',
pack=dict(ipadx=10, ipady=10, fill='both'), bd=5),
F2=dict(width=50, height=10, bg='green',
pack=dict(ipadx=10, ipady=10, fill='both')),
)
Container(root, widget=tk.Button,
B5=dict(width=30, height=40, bg='black',
fg='white', pack={}, text='Button1'),
B6=dict(width=50, height=10, bg='green', text='Button2',
place=dict(relx=0.5, rely=1, anchor='s')),
)
print(Container.__dict__)
root.mainloop()
A lot can be done and can be modified according to one's needs, these are just some approaches that I think will work very well to automate and keep a set of frames in shape and together.
There can be multiple ways to do this or maybe something better and efficient than these, feel free to give suggestions and share something new.
One solution to this problem, I think, as I don't fully understand your question, but this here was my solution:
import tkinter as tk
from tkinter import Frame,Button
class BaseClass(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.pack()
class Holder_frame(tk.Frame):
def __init__(self, master, frames=2):
tk.Frame.__init__(self, master)
self.master = master
self.frame_names = []
for i in range(frames):
Holder_frame.create_frames("F"+str(i+1), self)
#classmethod
def create_frames(cls, name, master):
setattr(cls, name, tk.Frame(master))
if __name__ == "__main__":
root = tk.Tk()
def raise1():
print(type(Holder_frame.F1))
def raise2():
print(type(Holder_frame.F2))
holder=Holder_frame(root,frames=2)
holder.grid(row=1,column=0)
b1 = tk.Button(root, text='1', command=raise1)
b1.grid(row=0,column=0)
b2 = tk.Button(root, text='2', command=raise2)
b2.grid(row=0,column=1)
print(Holder_frame.__dict__.items())
root.mainloop()
The use of setattr allows one to add variables to the class, just like if you were to type a function into the code. This allows you to access frames from outside the class as somewhat of a "global variable"
I used a file to test if it work outside as an imported module too:
# main.py
from nested_class import Holder_frame
import tkinter as tk
root = tk.Tk()
holder=Holder_frame(root,frames=1000)
holder.grid(row=1,column=0)
print(Holder_frame.__dict__.items())
root.mainloop()
I hope this answers your question,
James
EDIT:
After thinking there is, what I think, to be a cleaner system for what you want. With the code from this post one can see that your my written system could be replaced by a ttk.Notebook, and by removing the top bar by using style.layout('TNotebook.Tab', []), one can see that you would get a frame widget that could have frame widgets inside of it:
import tkinter as tk
import tkinter.ttk as ttk
class multiframe_example:
def __init__(self, master):
self.master = master
style = ttk.Style()
style.layout('TNotebook.Tab', [])
notebook = ttk.Notebook(self.master)
notebook.grid(row=0, column=0)
self.master.grid_rowconfigure(0, weight=1)
self.master.grid_columnconfigure(0, weight=1)
tab1 = tk.Frame(self.master, width=500, height=500, background="green")
tab2 = tk.Frame(self.master, width=500, height=500)
tab3 = tk.Frame(self.master, width=500, height=500)
notebook.add(tab1)
notebook.add(tab2)
notebook.add(tab3)
notebook.select(0) # select tab 1
notebook.select(1) # select tab 2
notebook.select(2) # select tab 3
def main():
root = tk.Tk()
root.geometry("500x500")
multiframe_example(root)
root.mainloop()
if __name__ == '__main__':
main()
Hope this code can support you and does as you would like!
I have an idea to create a simple scoreboard app (sport) with two windows (creating in Tkinter). One is for controlling and another one for output information.
So the idea is that I press button "show scoreboard" in a tk.Toplevel window and it appears in the main app window. And as I press hide, it hides. I know that I can create it just writing script without Classes and just like hundreds def strings but I want to use OOP as I would like to start programming the right way.
My problem is that when I press "Hide the scoreboard" (I create Labels for scoreboard) label is not hiding. Any suggestions?
I know that commands and defs must be in the same "tree", but how to arrange it when using the OOP.
So here is my code
import tkinter as tk
def forget():
scoreboard.pack_forget()
class Main(tk.Tk):
def __init__(self):
super().__init__()
self.title("Scorebug")
self.geometry("500x300")
self.configure(background="green")
scoreboard = tk.Label(self, text="This is like scoreboard")
scoreboard.pack()
class Control(tk.Toplevel):
def __init__(self):
super().__init__()
self.title("Controls")
self.geometry("100x300")
self.configure(background="red")
hidelabels = tk.Button(self, text="Hide the scoreboard", command=forget)
hidelabels.pack()
app = Main()
ctr = Control()
ctr.mainloop()
app.mainloop()
First you should use self.scoreboard to have access from other places.
self.scoreboard = tk.Label(self, text="This is like scoreboard")
self.scoreboard.pack()
and now you can remove it using
command=app.scoreboard.pack_forget
You can also send main window as argument to second window
ctr = Control(app)
class Control(tk.Toplevel):
def __init__(self, parent):
and then you can bind
command=parent.scoreboard.pack_forget
import tkinter as tk
class Main(tk.Tk):
def __init__(self):
super().__init__()
self.title("Scorebug")
self.geometry("500x300")
self.configure(background="green")
self.scoreboard = tk.Label(self, text="This is like scoreboard")
self.scoreboard.pack()
class Control(tk.Toplevel):
def __init__(self, parent):
super().__init__()
self.title("Controls")
self.geometry("100x300")
self.configure(background="red")
hidelabels = tk.Button(self, text="Hide the scoreboard", command=parent.scoreboard.pack_forget)
hidelabels.pack()
app = Main()
ctr = Control(app)
app.mainloop()
EDIT: You can also send only scireboard as argument to second window
ctr = Control(app.scoreboard)
and then you can bind
command=parent.pack_forget
Hello I think I have a python tkinter design issue. I have looked on this to structure my code better. I do not want to change the configuration of all my widgets individually and would like to use parent_widget.winfo_children() command that I found from this question.
I am wondering is there a better way to not individually configure the widgets and yet update their font and Style.
Here is my code and the current behavior:
class TabOne(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.tab1_note = ttk.Notebook(self,width=parent.winfo_screenwidth(), height=parent.winfo_screenheight())
tab1_open_observations = ttk.Frame(self.tab1_note)
tab1_closed_observations = ttk.Frame(self.tab1_note)
self.tab1_note.add(tab1_open_observations, text= "Open Projects")
self.tab1_note.add(tab1_closed_observations, text= "Closed/Deferred Projects")
self.tab1_note.pack()
self.tab_two_load(tab1_open_observations)
self.tab_three_load(tab1_closed_observations)
widget_list = []
widget_list.extend(tab1_open_observations.winfo_children())
widget_list.extend(tab1_closed_observations.winfo_children())
for wid in widget_list:
try:
wid.configure(font = 'helvetica 12')
except:
pass
def tab_one_load(self,tab1_refresh_db):
def tab_two_load(self,tab1_open_observations)
class TabTwo(Frame):
class TabThree(Frame):
class MainWindow(Frame):
def __init__(self, window, **kwargs):
Frame.__init__(self, window, **kwargs)
self.load_ui()
def load_ui(self):
self.note = ttk.Notebook(self,width=self.window.winfo_screenwidth()-(2*self.pad), height=self.window.winfo_screenheight()-(2*self.pad))
self.tab1 = TabOne(self.note)
self.tab2 = TabTwo(self.note)
self.tab3 = TabThree(self.note)
self.note.pack()
def main():
window = Tk()
window.title('Productivity Tool')
app = MainWindow(window)
app.pack(side="top", fill="both", expand=True)
window.mainloop()
if __name__ == '__main__':
main()
Current behavior for dependent drop down lists (code below):
The second list does not overlap if I add project_module_dropdown.configure(font='helvetica 12') below every dependent OptionsMenu, Here is more code for the function tab_two_load()
def tab_two_load(self,tab1_open_observations):
def update_modules(a,b,c):
proj_mo_names = [module[0] for module in project_modules]
proj_mod_select.set(proj_mo_names[0])
project_module_dropdown = OptionMenu(tab1_open_observations,proj_mod_select,*proj_mo_names)
project_module_dropdown.configure(font='helvetica 12')
project_module_dropdown.grid(row=2, column=1,padx=10, pady=10)
proj_select = StringVar(tab1_open_observations,value='Default Text')
proj_select.trace('w',update_modules)
proj_mod_select = StringVar(tab1_open_observations,value='Default Text')
proj_mod_select.trace('w',update_teams)
proj_mod_select.trace('w',update_artifacts)
proj_names = [project[1] for project in projects]
proj_select.set(proj_names[0])
project_dropdown = OptionMenu(tab1_open_observations,proj_select,*proj_names)
project_dropdown.grid(row=1,column=1,padx=10,pady=10)
I think the problem is with how I have structured my code but i believe i have compartmentalized the code well but I am open to suggestions. this is not exactly a code review problem. My problem is overlapping drowdowns, but I feel that I have some duplication in my code that I would like to avoid. Any help is great. Thanks.
I would like this as my desired behavior without adding project_module_dropdown.configure(font='helvetica 12'):
I was playing around with some Tkinter code that I found online:
from Tkinter import *
class ScrolledList(Frame):
def __init__(self, options, parent=None):
Frame.__init__(self, parent)
self.pack(expand=YES, fill=BOTH)
self.makeWidgets(options)
def handleList(self, event):
index = self.listbox.curselection()
label = self.listbox.get(index)
self.runCommand(label)
def makeWidgets(self, options):
sbar = Scrollbar(self)
list = Listbox(self, relief=SUNKEN)
sbar.config(command=list.yview)
list.config(yscrollcommand=sbar.set)
sbar.pack(side=RIGHT, fill=Y)
list.pack(side=LEFT, expand=YES, fill=BOTH)
pos = 0
for label in options:
list.insert(pos, label)
pos = pos + 1
list.bind('<Double-1>', self.handleList)
self.listbox = list
def runCommand(self, selection):
print 'You selected:', selection
if __name__ == '__main__':
options = map((lambda x: 'Lumberjack-' + str(x)), range(20))
ScrolledList(options).mainloop()
My question is: where is the frame created? I don't see anything like:
F1 = Tkinter.Frame()
Say if I wanted to add a label it would be:
label = Tkinter.Label(F1)
I'm looking into being able to add labels, and destroy the whole window when done (most likely add frame.destroy() line after print selection but I don't know what to address in that code).
Frames don't have titles; this works because Tkinter automagically creates a Tk instance the first time any widget is created and Tkinter detects that the root window hasn't been created yet (HT #BryanOakley). If you want to alter the window title, explicitly create a Tk instance and provide it as the parent to the ScrolledList:
if __name__ == '__main__':
options = map((lambda x: 'Lumberjack-' + str(x)), range(20))
app = Tk()
app.title('Demo')
ScrolledList(options, parent=app)
app.mainloop()
In many ways this is better, as it's easier to understand what's going on.
my question is were [sic] is the frame created?
A ScrolledList is a Frame, that's the whole point of inheritance (class ScrolledList(Frame): means "define a new class ScrolledList that inherits its behaviour from Frame"). So the frame is created by ScrolledList(...).
As #jonrsharpe points out, a ScrolledList is a Frame because the class is derived from it. The base Frame class is initialized in the first line of the ScrolledList.__init__() method:
class ScrolledList(Frame):
def __init__(self, options, parent=None):
Frame.__init__(self, parent) # <- calls base class constructor
...
Also, frames don't have a titles, so the closest way to have one is to add it to the window the frame is inside of. This can be done by explicitly creating the root window so you have a reference to it, use that to set its title, and then pass the window explicitly as the ScrolledList's parent:
if __name__ == '__main__':
root = Tk()
root.title('MyTitle')
root.minsize(200, 200) # also added so title is visible
options = map((lambda x: 'Lumberjack-' + str(x)), range(20))
ScrolledList(options, root) # <- Passes root window as the parent
root.mainloop()
I have a python-tkinter gui app that I've been trying to find some way to add in some functionality. I was hoping there would be a way to right-click on an item in the app's listbox area and bring up a context menu. Is tkinter able to accomplish this? Would I be better off looking into gtk or some other gui-toolkit?
You would create a Menu instance and write a function that calls
its post() or tk_popup() method.
The tkinter documentation doesn't currently have any information about tk_popup().
Read the Tk documentation for a description, or the source:
library/menu.tcl in the Tcl/Tk source:
::tk_popup --
This procedure pops up a menu and sets things up for traversing
the menu and its submenus.
Arguments:
menu - Name of the menu to be popped up.
x, y - Root coordinates at which to pop up the menu.
entry - Index of a menu entry to center over (x,y).
If omitted or specified as {}, then menu's
upper-left corner goes at (x,y).
tkinter/__init__.py in the Python source:
def tk_popup(self, x, y, entry=""):
"""Post the menu at position X,Y with entry ENTRY."""
self.tk.call('tk_popup', self._w, x, y, entry)
You associate your context menu invoking function with right-click via:
the_widget_clicked_on.bind("<Button-3>", your_function).
However, the number associated with right-click is not the same on every platform.
library/tk.tcl in the Tcl/Tk source:
On Darwin/Aqua, buttons from left to right are 1,3,2.
On Darwin/X11 with recent XQuartz as the X server, they are 1,2,3;
other X servers may differ.
Here is an example I wrote that adds a context menu to a Listbox:
import tkinter # Tkinter -> tkinter in Python 3
class FancyListbox(tkinter.Listbox):
def __init__(self, parent, *args, **kwargs):
tkinter.Listbox.__init__(self, parent, *args, **kwargs)
self.popup_menu = tkinter.Menu(self, tearoff=0)
self.popup_menu.add_command(label="Delete",
command=self.delete_selected)
self.popup_menu.add_command(label="Select All",
command=self.select_all)
self.bind("<Button-3>", self.popup) # Button-2 on Aqua
def popup(self, event):
try:
self.popup_menu.tk_popup(event.x_root, event.y_root, 0)
finally:
self.popup_menu.grab_release()
def delete_selected(self):
for i in self.curselection()[::-1]:
self.delete(i)
def select_all(self):
self.selection_set(0, 'end')
root = tkinter.Tk()
flb = FancyListbox(root, selectmode='multiple')
for n in range(10):
flb.insert('end', n)
flb.pack()
root.mainloop()
The use of grab_release() was observed in an example on effbot.
Its effect might not be the same on all systems.
I made some changes to the conext menu code above in order to adjust my demand and I think it would be useful to share:
Version 1:
import tkinter as tk
from tkinter import ttk
class Main(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
master.geometry('500x350')
self.master = master
self.tree = ttk.Treeview(self.master, height=15)
self.tree.pack(fill='x')
self.btn = tk.Button(master, text='click', command=self.clickbtn)
self.btn.pack()
self.aMenu = tk.Menu(master, tearoff=0)
self.aMenu.add_command(label='Delete', command=self.delete)
self.aMenu.add_command(label='Say Hello', command=self.hello)
self.num = 0
# attach popup to treeview widget
self.tree.bind("<Button-3>", self.popup)
def clickbtn(self):
text = 'Hello ' + str(self.num)
self.tree.insert('', 'end', text=text)
self.num += 1
def delete(self):
print(self.tree.focus())
if self.iid:
self.tree.delete(self.iid)
def hello(self):
print ('hello!')
def popup(self, event):
self.iid = self.tree.identify_row(event.y)
if self.iid:
# mouse pointer over item
self.tree.selection_set(self.iid)
self.aMenu.post(event.x_root, event.y_root)
else:
pass
root = tk.Tk()
app=Main(root)
root.mainloop()
Version 2:
import tkinter as tk
from tkinter import ttk
class Main(tk.Frame):
def __init__(self, master):
master.geometry('500x350')
self.master = master
tk.Frame.__init__(self, master)
self.tree = ttk.Treeview(self.master, height=15)
self.tree.pack(fill='x')
self.btn = tk.Button(master, text='click', command=self.clickbtn)
self.btn.pack()
self.rclick = RightClick(self.master)
self.num = 0
# attach popup to treeview widget
self.tree.bind('<Button-3>', self.rclick.popup)
def clickbtn(self):
text = 'Hello ' + str(self.num)
self.tree.insert('', 'end', text=text)
self.num += 1
class RightClick:
def __init__(self, master):
# create a popup menu
self.aMenu = tk.Menu(master, tearoff=0)
self.aMenu.add_command(label='Delete', command=self.delete)
self.aMenu.add_command(label='Say Hello', command=self.hello)
self.tree_item = ''
def delete(self):
if self.tree_item:
app.tree.delete(self.tree_item)
def hello(self):
print ('hello!')
def popup(self, event):
self.aMenu.post(event.x_root, event.y_root)
self.tree_item = app.tree.focus()
root = tk.Tk()
app=Main(root)
root.mainloop()
from tkinter import *
root=Tk()
root.geometry("500x400+200+100")
class Menu_Entry(Entry):
def __init__(self,perant,*args,**kwargs):
Entry.__init__(self,perant,*args,**kwargs)
self.popup_menu=Menu(self,tearoff=0,background='#1c1b1a',fg='white',
activebackground='#534c5c',
activeforeground='Yellow')
self.popup_menu.add_command(label="Cut ",command=self.Cut,
accelerator='Ctrl+V')
self.popup_menu.add_command(label="Copy ",command=self.Copy,compound=LEFT,
accelerator='Ctrl+C')
self.popup_menu.add_command(label="Paste ",command=self.Paste,accelerator='Ctrl+V')
self.popup_menu.add_separator()
self.popup_menu.add_command(label="Select all",command=self.select_all,accelerator="Ctrl+A")
self.popup_menu.add_command(label="Delete",command=self.delete_only,accelerator=" Delete")
self.popup_menu.add_command(label="Delete all",command=self.delete_selected,accelerator="Ctrl+D")
self.bind('<Button-3>',self.popup)
self.bind("<Control-d>",self.delete_selected_with_e1)
self.bind('<App>',self.popup)
self.context_menu = Menu(self, tearoff=0)
self.context_menu.add_command(label="Cut")
self.context_menu.add_command(label="Copy")
self.context_menu.add_command(label="Paste")
def popup(self, event):
try:
self.popup_menu.tk_popup(event.x_root, event.y_root, 0)
finally:
self.popup_menu.grab_release()
def Copy(self):
self.event_generate('<<Copy>>')
def Paste(self):
self.event_generate('<<Paste>>')
def Cut(self):
self.event_generate('<<Cut>>')
def delete_selected_with_e1(self,event):
self.select_range(0, END)
self.focus()
self.event_generate("<Delete>")
def delete_selected(self):
self.select_range(0, END)
self.focus()
self.event_generate("<Delete>")
def delete_only(self):
self.event_generate("<BackSpace>")
def select_all(self):
self.select_range(0, END)
self.focus()
ent=Menu_Entry(root)
ent.pack()
root.mainloop()
Important Caveat:
(Assuming the event argument that contains the coordinates is called "event"): Nothing will happen or be visible when you call tk_popup(...) unless you use "event.x_root" and "event.y_root" as arguments. If you do the obvious of using "event.x" and "event.y", it won't work, even though the names of the coordinates are "x" and "y" and there is no mention of "x_root" and "y_root" anywhere within it.
As for the grab_release(..), it's not necessary, anywhere. "tearoff=0" also isn't necessary, setting it to 1 (which is default), simply adds a dotted line entry to the context menu. If you click on it, it detaches the context menu and makes it its own top-level window with window decorators. tearoff=0 will hide this entry. Moreover, it doesn't matter if you set the menu's master to any specific widget or root, or anything at all.