I am using Python 3.3 and tkinter to make a GUI interface for a pedestrian fleeing simulation.
I've written two simulation programs, and they worked well. However,I got stuck when trying to call them from my main application. I want the simulation window to appear in a separate window (create a child window of the main window).
#flee_GUI.py
#!/usr/bin/env python
import tkinter
class MenuBar(tkinter.Menu):
def __init__(self,parent):
tkinter.Menu.__init__(self,parent)
###File###
fileMenu = tkinter.Menu(self, tearoff=False)
self.add_cascade(label="File",underline=0, menu=fileMenu)
fileMenu.add_command(label='Open',underline=1)
fileMenu.add_separator()
fileMenu.add_command(label="Exit", underline=1, command=self.quit)
###Run###
runMenu=tkinter.Menu(self,tearoff=False)
self.add_cascade(label='Run',underline=1,menu=runMenu)
runMenu.add_command(label='Open Bounary Model',underline=1,command=runModel1)
class Frame(tkinter.Tk):
def __init__(self,parent):
tkinter.Frame.__init__(self,parent)
self.parent=parent
def runModel1():
from drawcanvas_Alpha_7_0_open_border import cell
I=cell(None)
class App(tkinter.Tk):
def __init__(self,parent):
tkinter.Tk.__init__(self,parent)
self.parent=parent
runModel1()
menubar=MenuBar(self)
self.config(menu=menubar)
if __name__=='__main__':
app=App(None)
app.mainloop()
#drawcanvas_Alpha_7_0_open_border.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Run this by python3.x.
#If you are using Python2.x,be aware of the difference such as print,Tkinter and tkinter,etc.
#from tkinter import *
import tkinter
import random
#class cell(Frame):
class cell(tkinter.Tk):
def __init__(self,parent):
tkinter.Tk.__init__(self,parent)
self.parent=parent
self.channel_length=40#aisle length (1 unit for 10 pixel)
self.channel_width=40#aisle width
self.origin_x=0
self.origin_y=0
self.pixel_unit=10
self.speed=100
self.alltime=0
self.PN=100#Number of pedestrian
self.Pu=0.1
self.Pd=0.9#probability of going down if the right side were occupied
self.window_width=self.origin_x+self.channel_length*self.pixel_unit
self.window_height=self.origin_y+self.channel_width*self.pixel_unit+2*self.pixel_unit
self.canvas=tkinter.Canvas(
self.parent,width=self.window_width,height=self.window_height,bg='lightblue')
self.Ped_x=[]
self.Ped_y=[]
self.block_flag=[]
self.block_occupy=[]
self.draw_canvas()
self.draw_grid()
self.draw_architecture()
self.draw_pedestrian_init()
self.draw_pedestrian()
def draw_canvas(self):
self.canvas.pack()
def destroy_canvas(self):
self.canvas.destroy()
def destroy_architecture(self):
pass
def draw_grid(self):
for i in range(2,self.channel_width+1):
self.draw_line(0,i,self.channel_length,i)
for i in range(1,self.channel_length):
self.draw_line(i,0,i,self.channel_width+1)
def draw_line(self,x0,y0,x1,y1,linedash=1):
self.canvas.create_line(
self.origin_x+x0*self.pixel_unit,
self.origin_y+y0*self.pixel_unit,
self.origin_x+x1*self.pixel_unit,
self.origin_y+y1*self.pixel_unit,dash=linedash)
def draw(self,x0,y0,x1,y1,color='black'):
self.canvas.create_rectangle(
self.origin_x+x0*self.pixel_unit,
self.origin_y+y0*self.pixel_unit,
self.origin_x+x1*self.pixel_unit,
self.origin_y+y1*self.pixel_unit,
fill=color)
for i in range(y0,y1):
for j in range(x0,x1):
self.block_occupy[i][j]=1
#print(j,i)
def draw_architecture(self):
for i in range(0,(self.channel_width+1)+1):
self.block_occupy.append([])
for j in range(0,self.channel_length):
self.block_occupy[i].append(0)
self.draw(0,0,self.channel_length,1)
self.draw(0,self.channel_width+1,self.channel_length,self.channel_width+2)
self.draw(30,1,31,int(self.channel_width/2-1),'red')
#self.draw(30,int(self.channel_width/2+1),31,self.channel_width+1,'red')
def draw_pedestrian_init(self):
Ped_count=0
while Ped_count<self.PN:
self.Ped_x.append(
int(random.randrange(
self.origin_x,self.origin_x+30*self.pixel_unit)/self.pixel_unit)*self.pixel_unit)
self.Ped_y.append(
int(random.randrange(
self.origin_y+self.pixel_unit,self.origin_y+(self.channel_width+1)*self.pixel_unit)/self.pixel_unit)*self.pixel_unit)
tmp_x=int((self.Ped_x[Ped_count]-self.origin_x)/self.pixel_unit)
tmp_y=int((self.Ped_y[Ped_count]-self.origin_y)/self.pixel_unit)
if self.block_occupy[tmp_y][tmp_x]==1:
self.Ped_x.pop()
self.Ped_y.pop()
else:
self.block_occupy[tmp_y][tmp_x]=1
Ped_count=Ped_count+1
self.block_flag=[self.canvas.create_rectangle(self.Ped_x[i],self.Ped_y[i],
self.Ped_x[i]+self.pixel_unit,self.Ped_y[i]+self.pixel_unit,fill='green') for i in range(0,self.PN)]
def draw_pedestrian(self):
for i in range(0,self.PN):
self.canvas.delete(self.block_flag[i])
#print(self.block_occupy)
#count_f=self.PN
for i in range(0,self.PN):
if self.Ped_x[i]>self.origin_x+(self.channel_length-1)*self.pixel_unit-1:
#self.Ped_x[i]=self.Ped_x[i]-self.channel_length*self.pixel_unit
dummy_x=int((self.Ped_x[i]-self.origin_x)/self.pixel_unit)
dummy_y=int((self.Ped_y[i]-self.origin_y)/self.pixel_unit)
self.block_occupy[dummy_y][dummy_x]=0
#count_f=self.PN-1
self.Ped_x[i]=-1
self.Ped_y[i]=-1
temp_block_flag1=[]
temp_block_flag2=[]
for i in range(0,self.PN):
if self.Ped_x[i]!=-1:
temp_block_flag1.append(self.block_flag[i])
else:
temp_block_flag2.append(self.block_flag[i])
self.block_flag=temp_block_flag1
for i in range(0,len(temp_block_flag2)):
self.canvas.delete(temp_block_flag2[i])
self.Ped_x=[self.Ped_x[i] for i in range(0,self.PN) if self.Ped_x[i]!=-1]
self.Ped_y=[self.Ped_y[i] for i in range(0,self.PN) if self.Ped_y[i]!=-1]
self.PN=len(self.Ped_x)
for i in range(0,self.PN):
print(self.PN,i,len(self.Ped_x))
tmp_x=int((self.Ped_x[i]-self.origin_x)/self.pixel_unit)
tmp_y=int((self.Ped_y[i]-self.origin_y)/self.pixel_unit)
if self.block_occupy[tmp_y][tmp_x+1]==0:
self.block_occupy[tmp_y][tmp_x]=0
self.block_occupy[tmp_y][tmp_x+1]=1
self.Ped_x[i]=self.Ped_x[i]+self.pixel_unit
elif (self.block_occupy[tmp_y+1][tmp_x]==0
and self.block_occupy[tmp_y-1][tmp_x]==0):#The right side is occupied,while the up and down side is free
if random.uniform(0,1)<self.Pd:#go down
self.block_occupy[tmp_y][tmp_x]=0
self.block_occupy[tmp_y+1][tmp_x]=1
self.Ped_y[i]=self.Ped_y[i]+self.pixel_unit
else:#go up
self.block_occupy[tmp_y][tmp_x]=0
self.block_occupy[tmp_y-1][tmp_x]=1
self.Ped_y[i]=self.Ped_y[i]-self.pixel_unit
elif (self.block_occupy[tmp_y+1][tmp_x]==1 #the up side is occupied,while the down side is free
and self.block_occupy[tmp_y-1][tmp_x]==0):
self.block_occupy[tmp_y][tmp_x]=0
self.block_occupy[tmp_y-1][tmp_x]=1
self.Ped_y[i]=self.Ped_y[i]-self.pixel_unit
elif (self.block_occupy[tmp_y+1][tmp_x]==0 #the up side is free,while the down side is occupied
and self.block_occupy[tmp_y-1][tmp_x]==1):
self.block_occupy[tmp_y][tmp_x]=0
self.block_occupy[tmp_y+1][tmp_x]=1
self.Ped_y[i]=self.Ped_y[i]+self.pixel_unit
#print(self.block_occupy)
self.block_flag=[self.canvas.create_rectangle(self.Ped_x[i],self.Ped_y[i],
self.Ped_x[i]+self.pixel_unit,self.Ped_y[i]+self.pixel_unit,fill='green') for i in range(0,self.PN)]
self.alltime+=1
self.after(self.speed,self.draw_pedestrian)
if self.PN==0:
print("Fleeing complete!,total time:",self.alltime)
self.destroy_canvas()
if __name__=='__main__':
Io=cell(None)
Io.mainloop()
How can I launch a child window from my main application with tkinter?
You create child windows by creating instances of Toplevel. See http://effbot.org/tkinterbook/toplevel.htm for more information.
Here's an example that lets you create new windows by clicking on a button:
import Tkinter as tk
class MainWindow(tk.Frame):
counter = 0
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.button = tk.Button(self, text="Create new window",
command=self.create_window)
self.button.pack(side="top")
def create_window(self):
self.counter += 1
t = tk.Toplevel(self)
t.wm_title("Window #%s" % self.counter)
l = tk.Label(t, text="This is window #%s" % self.counter)
l.pack(side="top", fill="both", expand=True, padx=100, pady=100)
if __name__ == "__main__":
root = tk.Tk()
main = MainWindow(root)
main.pack(side="top", fill="both", expand=True)
root.mainloop()
Related
Creates two windows and gridding is not correct. Some additional comments in the code initiation.
I have used this approach, without the super init with no problem, many times.
Advice appreciated.
Thanks
# timhockswender#gmail.com
import tkinter as tk
from tkinter import ttk
class constants_page(tk.Frame):
def __init__(self):
super(constants_page, self).__init__() # from stackoverflow
# if not used error = 'constants_page' object has no attribute 'tk'
# if used, another tiny window is opened
# in addtion to the constants_page
self.constants_page = tk.Tk()
self.constants_page.geometry("1000x500") #width*Length
self.constants_page.title("Owen's Unit Conversion App")
self.constants_page.configure(background='light blue')
self.CreateWidgets()
def CreateWidgets(self):
self.value_label = ttk.Label(self.constants_page,text="Value----->" , width =10 )
self.value_label.grid(row=0, column=1, columnspan=1, sticky='nse')
# Problem: not gridding properly
self.title_label = ttk.Label(self.constants_page, text="Important Physical Constants",
anchor=tk.CENTER, font=("Arial",20)).grid(row=2, columnspan=2)
for r in range(2):
self.constants_page.rowconfigure(r, weight=1, uniform='row')
for c in range(2):
self.constants_page.columnconfigure(c, weight=1 )
def Show_Page():
# Create the entire GUI program
program = constants_page()
program.mainloop()
if __name__ == "__main__":
Show_Page()
The super call expects you to provide a root window (an instance of tk.Tk()). If you don't provide one it defaults to the first root window opened, and if none has been opened yet then it helpfully opens one for you. A few lines later you open a second one yourself.
The easy fix is to remove the self.constants_page = tk.Tk() line. The proper fix is to make the Tk() instance outside of the class and pass it in. This allows you to use the Frame class itself to lay out widgets (use self instead of self.constants_page). Try this:
import tkinter as tk
from tkinter import ttk
class constants_page(tk.Frame):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
master.geometry("1000x500") #width*Length
master.title("Owen's Unit Conversion App")
self.configure(background='light blue')
self.CreateWidgets()
def CreateWidgets(self):
self.value_label = ttk.Label(self,text="Value----->" , width =10 )
self.value_label.grid(row=0, column=1, columnspan=1, sticky='nse')
self.title_label = ttk.Label(self, text="Important Physical Constants",
anchor=tk.CENTER, font=("Arial",20)).grid(row=2, columnspan=2)
for r in range(2):
self.rowconfigure(r, weight=1, uniform='row')
for c in range(2):
self.columnconfigure(c, weight=1 )
def Show_Page():
# Create the entire GUI program
program = tk.Tk()
win = constants_page(program)
win.pack()
program.mainloop()
if __name__ == "__main__":
Show_Page()
MainTicTacToe.py
import tkinter as tk
import MenubarCommand as mbc
class Game(tk.Frame):
def __init__(self,parent):
tk.Frame.__init__(self,parent)
self.parnt=parent
# self.parnt.geometry('500x300')
self.parnt.title("Tic Tac Toe")
# self.pack()
menubar=tk.Menu(parent)
# 'settings' menu
settingsOption=tk.Menu(menubar, tearoff=0)
settingsOption.add_command(label='Player Settings', command=self.doit)
settingsOption.add_command(label='Board Settins', command=self.doit)
menubar.add_cascade(label='Setings', menu=settingsOption)
# without using this method, menubar isn't shown in Frame
self.parnt.config(menu=menubar)
def doit(self):
root=self.win()
set=mbc.playerSettings(root)
# print(set.p1name)
root.mainloop()
def win(self):
return tk.Tk()
def main():
root=tk.Tk()
Game(root)
root.mainloop()
main()
MenubarCommand.py
import tkinter as tk
class playerSettings(tk.Frame):
def __init__(self,parent=tk.Frame):
tk.Frame.__init__(self,parent)
self.parnt=parent
self.parnt.title("Player Setting")
self.p1name='Player 1'
self.p2name='Player 2'
self.p1symbol='x'
self.p2symbol='o'
# **********************************#
self.p1NameLabel = tk.Label(parent, text='Player 1: Name ')
self.p1NameLabel.grid(row=0, column=0)
self.p1NameEntry = tk.Entry(parent)
self.p1NameEntry.insert(0,self.p1name)
self.p1NameEntry.bind('<FocusIn>', lambda event:self.p1NameEntry.delete(0,'end'))
self.p1NameEntry.grid(row=0, column=1)
apply=tk.Button(parent, text="Apply Settings", fg='white', bg='gray', command=self.saveStat)
apply.grid(row=2, rowspan=1, columnspan=4)
def saveStat(self):
print('Settings Window Destroyed')
self.p1name=self.p1NameEntry.get()
print(self.p1name)
self.parnt.destroy()
I want to change the value of attribute of an instance in one file from the instance of another class in another file already created.
When I change default Player name in MenubarComman.py file, I want to access the changed name from MainTicTacToe.py class. How can I do this?
I'm new new in Python.
Thanks in Advance.
You problems stem from 2 instances of Tk(), and sloppy programming, i.e. sometimes you use parent, and sometimes self.parnt which is a bad habit to get into, so everything is changed to self.top so an error will pop up if any of those two remains.. You also have to have a way to signal when PlayerSetting() is finished. The way the program is structured, the only way that I know of is to check for "alive" using recursion. You could also have PlayerSettings call a function in Game() when finished, which would print the value. I have cleaned up your code and it works as I understand it should.. Note that the 2 classes are in the same file to make it easier to test and post here.
import tkinter as tk
##import MenubarCommand as mbc
class Game():
def __init__(self,parent):
self.parnt=parent
# self.parnt.geometry('500x300')
self.parnt.title("Tic Tac Toe")
# self.pack()
menubar=tk.Menu(parent)
# 'settings' menu
settingsOption=tk.Menu(menubar, tearoff=0, bg="yellow")
settingsOption.add_command(label='Player Settings', command=self.doit)
settingsOption.add_command(label='Board Settins', command=self.doit)
menubar.add_cascade(label='Setings', menu=settingsOption)
# without using this method, menubar isn't shown in Frame
self.parnt.config(menu=menubar)
def doit(self):
## do not open more than one Tk() instance
##root=self.win()
self.top=tk.Toplevel(self.parnt)
self.set_it=PlayerSettings(self.top)
self.get_variable()
##root.mainloop()
def get_variable(self):
""" check continuously if PlayerSettings has exited and
if so, get the Entry's value
"""
if self.set_it:
self.parnt.after(1000, self.get_variable)
else:
print("from Game", self.set_it.p1name)
def win(self):
return tk.Tk()
class PlayerSettings():
def __init__(self, parent):
self.top=parent
self.p1name='Player 1'
self.p2name='Player 2'
self.p1symbol='x'
self.p2symbol='o'
# **********************************#
self.p1NameLabel = tk.Label(self.top, text='Player 1: Name ', bg="lightblue")
self.p1NameLabel.grid(row=0, column=0)
self.p1NameEntry = tk.Entry(self.top)
self.p1NameEntry.focus_set()
self.p1NameEntry.insert(0,self.p1name)
##self.p1NameEntry.bind('<FocusIn>', lambda event:self.p1NameEntry.delete(0,'end'))
self.p1NameEntry.grid(row=0, column=1, sticky="nsew")
apply=tk.Button(self.top, text="Apply Settings", fg='white', bg='gray', command=self.saveStat)
apply.grid(row=2, rowspan=1, columnspan=4)
def saveStat(self):
self.p1name=self.p1NameEntry.get()
print(self.p1name)
print('Settings Window Destroyed')
self.top.destroy()
root=tk.Tk()
Game(root)
root.mainloop()
I want my GUI to have a 'new window' option that will be just the same as the first one.
The problem is that it also has an exit(quit) button that won't work as it should - whenever I open the new window and then press the button, in the first click nothing happens and in the second one it closes both windows (if 3 windows are open so it'll close everything in the third click and so on).
This the relevant code:
from Tkinter import *
from ttk import *
class Application(Tk):
def __init__(self):
self.root = Tk()
self.root.geometry("250x150")
self.app = Frame(self.root)
self.app.grid()
self.create_menu()
self.create_widgets()
self.root.mainloop()
def create_menu(self):
menu = Menu(self.root)
self.root.config(menu=menu)
sub_menu = Menu(menu)
menu.add_cascade(label="File", menu=sub_menu)
sub_menu.add_command(label="New", command=self.__init__)
sub_menu.add_command(label="Run", command=self.enter)
sub_menu.add_separator()
sub_menu.add_command(label="Exit", command=self.app.quit)
I also tried to change:
sub_menu.add_command(label="New", command=self.__init__)
to:
sub_menu.add_command(label="New", command=self.new window)
Where:
def new_window(self):
class App(Application):
Application.__init__(self)
Both do the same thing.
How can I fix it?
In a Tkinter-Application there may only be one Tk-object. If the object is destroyed or destroyed by the garbage collector, Tkinter will be disabled. Use Toplevel for other other windows instead.
Try this instead:
from Tkinter import *
from ttk import *
class Application(object):
def __init__(self, master):
self.root = master
self.root.geometry("250x150")
self.app = Frame(self.root)
self.app.grid()
self.create_menu()
self.create_widgets()
def create_menu(self):
menu = Menu(self.root)
self.root.config(menu=menu)
sub_menu = Menu(menu)
menu.add_cascade(label="File", menu=sub_menu)
sub_menu.add_command(label="New", command=self.new)
sub_menu.add_command(label="Run", command=self.enter)
sub_menu.add_separator()
sub_menu.add_command(label="Exit", command=self.quit)
def new(self):
window = Toplevel(tk)
return Application(window)
def quit(self):
tk.destroy()
tk = Tk()
Application(tk)
tk.mainloop()
I want to create a GUI that shows a message and it is automatically destroyed after some time. I saw this question in different posts but none of the solutions proposed worked out for my App. Here a small part of the code
class MessageShort(tkSimpleDialog.Dialog):
def __init__(self, parent, text, time):
self.top=Toplevel.__init__(self, parent)
self.transient(parent)
self.parent = parent
self.text=text
self.time=time
body = Frame(self)
self.initial_focus = self.body(body)
body.pack(padx=10, pady=10)
if not self.initial_focus:
self.initial_focus = self
self.geometry("+%d+%d" % (parent.winfo_rootx()+200,
parent.winfo_rooty()+75))
self.initial_focus.focus_set()
self.wait_window(self)
def body(self, master):
m=Message(master, text=self.text).grid(row=0,column=0,sticky=W)
master.after(self.time,master.destroy())
MessageShort(root,"Select date and decimal format",2000)#call from another part to the class to create the GUI message
root = Tk()
app = App(root) #main App
root.mainloop()
The App have different Menus and Tkinter classes to display the different tools
With the current code I close the App and I just want to close the message but not the App
Create a timer and set destroying the root as it's callback:
from threading import Timer
import time
def called_after_timer():
if(root != None):
root.destroy()
t = Timer(20 * 60, called_after_timeout)
t.start()
# do something else, such as
time.sleep(1500)
Finally I got something that seems to work
class MessageShort(tkSimpleDialog.Dialog):
def __init__(self, parent, text, time):
self.top=Toplevel.__init__(self, parent)
self.transient(parent)
self.parent = parent
self.text=text
self.time=time
body = Frame(self)
self.initial_focus = self.body(body)
body.pack(padx=10, pady=10)
if not self.initial_focus:
self.initial_focus = self
self.geometry("+%d+%d" % (parent.winfo_rootx()+200,
parent.winfo_rooty()+75))
self.initial_focus.focus_set()
self.wait_window(self)
def body(self, master):
m=Message(master, text=self.text).grid(row=0,column=0,sticky=W)
master.after(self.time,self.destroy)
MessageShort(root,"Select date and decimal format",2000)#call from another part to the class to create the GUI message
root = Tk()
app = App(root) #main App
root.mainloop()
In the last line self.master.destroy destroys m but not the container itself. So it has to call to self.destroy
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.