I am green in Python, after going through books and the questions here, I tried to code the following. The following is my simplified version of my code. this is a script for GUI. In my design, there would be a "back" button on the top, and 10 page buttons below it. And when I clicked the button, it will go to another page. But I replace it by showing the string of the picture instead.
However, I found that the "back" button is at the bottom now. I believe it goes wrong on Line 12.
I tried as "PageButton=ttk.Button(self, text=self.i, command=lambda: print(self.PicLink))" as well, but all page buttons disappeared. I am totally lost on what should be set to get back my designed pattern. Although it still work funtionally, I would like to know how should I edit to meet my orignial dseign, So I can improve my coding knowledge and problem solving skill. Thanks for all your effort in advance.
import tkinter as tk
from tkinter import ttk
class PageButton(ttk.Frame):
def __init__(self, i, PicLink):
self.i = i+1
self.row=i//3+1
self.column=i%3
self.PicLink=PicLink
super().__init__()
print(self.i, self.row, self.column)
PageButton=ttk.Button(text=self.i, command=lambda: print(self.PicLink))
PageButton.grid(row=self.row, column=self.column)
class ViewerFrame(ttk.Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
ViewerFrame.backButton=ttk.Button(self, text="Back", command=lambda: BackPage(parent))
ViewerFrame.backButton.grid(row=0, column=0)
for i in range(10):
PicLink="abc%04i.jpg" % (i+1)
globals()['Button%s' % (i+1)]=PageButton(i, PicLink)
class ViewerPage(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title("Viewer")
self.resizable(width=False, height=False)
ViewerFrame(self).grid(sticky=(tk.E+tk.W+tk.N+tk.S))
def ViewerWindow():
app=ViewerPage()
app.mainloop()
if __name__=="__main__":
ViewerWindow()
Since you do not specify the parent when creating the button inside PageButton class, it will be a child of the root window instead and put inside the root window instead of instance of PageButton.
Those page buttons are created and put into the root window before the instance of ViewerPage class which includes the Back button, so the Back button is being put after those page buttons.
So it is better to specify the parent when creating those page buttons and also PageButton should inherit from ttk.Button directly instead of creating an instance of it inside.
Below is the modified code:
import tkinter as tk
from tkinter import ttk
# inherit from ttk.Button directly
class PageButton(ttk.Button):
# added parent argument
def __init__(self, parent, i, PicLink):
self.i = i+1
self.row=i//3+1
self.column=i%3
self.PicLink=PicLink
print(self.i, self.row, self.column)
# specify the parent
super().__init__(parent, text=self.i, command=lambda: print(self.PicLink))
self.grid(row=self.row, column=self.column)
class ViewerFrame(ttk.Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.backButton=ttk.Button(self, text="Back", command=lambda: BackPage(parent))
self.backButton.grid(row=0, column=0)
# use a dictionary to store those page buttons
self.buttons = {}
for i in range(10):
PicLink="abc%04i.jpg" % (i+1)
self.buttons[f'Button{i+1}'] = PageButton(self, i, PicLink) # specify the parent
class ViewerPage(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title("Viewer")
self.resizable(width=False, height=False)
ViewerFrame(self).grid(sticky=(tk.E+tk.W+tk.N+tk.S))
def ViewerWindow():
app=ViewerPage()
app.mainloop()
if __name__=="__main__":
ViewerWindow()
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
Following Bryan Oakley's answer to this post Best way to structure a tkinter application?, I would like to move the contents of tab1 to its own class, but leave say_hello and get_item in the main class as they are. How do I do that?
import tkinter as tk
from tkinter import ttk, N, S, END, EXTENDED
class MainApplication(tk.Frame):
def __init__(self, root, *args, **kwargs):
tk.Frame.__init__(self, root, *args, **kwargs)
self.root = root
self.nb = ttk.Notebook(root)
self.tab1 = ttk.Frame(self.nb)
self.tab2 = ttk.Frame(self.nb)
self.nb.add(self.tab1, text='TAB1')
self.nb.add(self.tab2, text='TAB2')
self.nb.grid(row=0, column=0)
#contents of tab1 - which I would like to place in their own class
self.frame = tk.Frame(self.tab1)
self.frame.grid(row=0, column=0)
self.button = tk.Button(self.frame, text='Hello', command=self.say_hello)
self.button.grid(row=0, column=0)
self.items = ['A','B','C']
self.listbox = tk.Listbox(self.frame, selectmode=EXTENDED, exportselection=0)
self.listbox.grid(row=1, column=0)
self.scrollbar = tk.Scrollbar(self.frame, orient='vertical')
self.scrollbar.grid(row=1, column=1, sticky=N+S)
self.listbox.config(yscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.listbox.yview)
self.listbox.bind('<<ListboxSelect>>', self.get_item)
for item in self.items:
self.listbox.insert(END, item)
#contents of tab2 - which should be in their own class
#...
#...
def say_hello(self):
print('hello')
def get_item(self, evt):
w = evt.widget
index = int(w.curselection()[0])
print('item selected = {}'.format(self.items[index]))
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root)
root.mainloop()
EDIT:
Thank you Saad for your detailed response. I ran your code, studied it, and learned quite a bit. However, I modified my question to make it more focused.
According to me, I think everyone has their own style of writing and organizing their files and classes. My way of organizing code might not be the best. But I'll try my best to make it organized and simpler for you. Though there is no hard and fast rule on how to organize any Tkinter application. But yes OOP is the best way to organize big projects as they make the code small and easier to understand.
You can create a class for each tab (Tab1, Tab2, ..) in a separate file (tab1.py, tab2.py, ..) but I would rather have all my tabs in one file named as tabs.py. So I can import them like so..
import tabs
tabs.Tab1()
tabs.Tab2()
...
Kindly go through each comment in the code and my points to get most of your answers.
I've divided your code into 4 parts.
First I made a base frame class for all tabs. In that base frame class, we can configure things that all tabs require in common. For example, let's say you want a frame in each tab that has background color 'lightyellow'. So you can create a base class and put it in a separate file as baseframe.py. like so...
# baseframe.py
import tkinter as tk
class BaseFrame(tk.Frame):
"This is a base frame for Tabs."
def __init__(self, master=None, **kw):
super().__init__(master=master, **kw)
# This will be your baseframe, if you need it.
# Do something here that is common for all tabs.
# for example if you want to have the same color for every tab.
# These are just some example, change it as per your need.
self['bg'] = 'lightyellow'
self['relief'] = 'sunken'
self['borderwidth'] = 5
...
def show_info(self):
"This function is common to every tab"
# You don't necessarily need this,
# This is just to show you the possibilities.
# Do something in this function, that you want to have in every tab.
...
...
print('show_info', self)
return
Would it make sense to have a separate file with a function in it for creating a generic Listbox?
I think a CustomListbox class would be nice, you can be very creative in creating this class but I kept it simple. I added a scrollbar inside of CustomListbox class and configured it.
You can either create file customlistbox.py for this. or change the name of baseframe.py to basewidgets.py and keep both BaseFrame and CustomListbox in one file.
# customlistbox.py
import tkinter as tk
class CustomListbox(tk.Listbox):
"Tkinter listbox which has vertical scrollbar configured."
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master=master, cnf=cnf, **kw)
# Config scrollbar
self.scrollbar = tk.Scrollbar(self.master,orient='vertical',command=self.yview)
self.config(yscrollcommand=self.scrollbar.set)
See in tab classes how to use it.
Here's tabs.py file,
# tabs.py
import tkinter as tk
from baseframe import BaseFrame
from customlistbox import CustomListbox
from tkinter import ttk, N, S, END, EXTENDED
class Tab1(BaseFrame):
"This is Tab 1."
def __init__(self, master=None, **kw):
super().__init__(master=master, **kw)
self.button2 = tk.Button(self,text='Welcome to Tab1', command=self.show_info)
self.button2.grid(row=0, column=0)
self.button = tk.Button(self, text='Hello', command=self.say_hello)
self.button.grid(row=1, column=0)
self.items = ['A','B','C']
self.listbox = CustomListbox(self, selectmode=EXTENDED, exportselection=0)
self.listbox.grid(row=2, column=0)
self.listbox.scrollbar.grid(row=2, column=1, sticky=N+S)
self.listbox.bind("<<ListboxSelect>>", self.get_item)
for item in self.items:
self.listbox.insert(END, item)
def say_hello(self):
"Callback function for button."
print('hello')
def get_item(self, evt):
"Internal function."
w = evt.widget
index = int(w.curselection()[0])
print('item selected = {}'.format(self.items[index]))
class Tab2(BaseFrame):
"This is Tab2."
def __init__(self, master=None, **kw):
super().__init__(master=master, **kw)
self.button = tk.Button(self,text='Welcome to Tab2', command=self.show_info)
self.button.grid(row=0, column=0)
Finally comes the main.py file. Here you can import your tabs (import tabs). I organized the MainApplication class according to my comfort. For example, rather than creating each instance (self.tab1, self.tab2,..) for tabs, I created a list self.tabs to contain all the tabs, which is more convenient to access through the index of self.tabs.
# main.py
import tkinter as tk
import tabs as tb
from tkinter import ttk, N, S, END, EXTENDED
class MainApplication(ttk.Notebook):
def __init__(self, master=None, **kw):
super().__init__(master=master, **kw)
self.master = master
self.tabs = [] # All tabs list, easier to access.
# Add tabs here.
self.add_tab(tb.Tab1, text='TAB1')
self.add_tab(tb.Tab2, text='TAB2')
...
...
# Excess methods of Tabs:-
self.tabs[0].say_hello()
self.tabs[1].show_info()
# for example: Bind say_hello() with "Button-2" to this class.
self.bind('<Button-2>', lambda evt: self.tabs[0].say_hello())
def add_tab(self, tab, **kw):
"Adds tab to the notebook."
self.tabs.append(tab(self))
self.add(self.tabs[-1], **kw)
if __name__ == "__main__":
root = tk.Tk()
# Creating an container.
container = tk.Frame(root)
container.grid()
# Initializing the app.
app = MainApplication(container)
app.grid()
root.mainloop()
Hopefully, this has made a lot of things clear to you.
I am writing a simple GUI using tkinter in Python3. It allows the user to press a button which calls a function, select_files, that lets the user select a bunch of files. The function processes the files and outputs a tuple of three things that need to be assigned to class variables, say a, b, c, for further processing. These variables should be accessible to any function that I define inside the class.
I attempted the following, but I get name 'self' is not defined error. The function select_files is already defined and written in a separate .py file and I am importing it from the correct location. I'm relatively new to GUI programming and object-oriented programming in general.
I studied a similar question asked here: python tkinter return value from function used in command, which was helpful in general, but couldn't help me with my specific problem.
Should these self.a, self.b, self.c variables appear under the __init__() method? If so, how do I assign them to the return values of select_files function? Also, would this make the function open_files unnecessary?
from ... import select_files
import tkinter as tk
from tkinter import filedialog
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.run_button()
def open_files(self):
self.a, self.b, self.c = select_files()
print(self.a) #self is not defined error comes from here
def run_button(self):
self.button = tk.Button(root, text = 'run', command = self.open_files)
self.button.pack()
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
EDIT: based on rassar's comment, I made the following change, and it works. The user can press the print button, and the object gets printed. Can someone now explain why self.run_button() and self.print_button() need to be in the init method?
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.run_button()
self.print_button()
def open_files(self):
self.a, self.b, self.c = select_files()
def print_files(self):
print(self.a)
def run_button(self):
self.button = tk.Button(root, text = 'run', command = self.open_files)
self.button.pack()
def print_button(self):
self.button = tk.Button(root, text = 'print', command = self.print_files)
self.button.pack()
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
I am creating a card game in tkinter and need help with referencing to the frame names. My problem is that when I want to "refresh" the frame, I need to destroy and recreate it and this changes the progressive numbering of the frames.
Please take a look at the code below. The example shows that the third frame every time gets a new name as it gets recreated.
import tkinter as tk
class RootFrame(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.main_window = tk.Frame(self)
self.main_window.pack(side="top", fill="both", expand=True)
self.main_label = tk.Label(self.main_window, text="Main Window")
self.main_label.pack()
self.second_frame = SecondFrame(self.main_window, self)
self.second_frame.pack()
class SecondFrame(tk.Frame):
def __init__(self, parent, controller, *args, **kwargs):
super().__init__(*args, **kwargs)
self.controller = controller
label = tk.Label(self, text="Second Frame")
label.pack()
self.create_third_frame()
def create_third_frame(self):
self.third_frame = ThirdFrame(self, self.controller)
self.third_frame.pack()
def update_frame(self):
self.third_frame.destroy()
self.create_third_frame()
class ThirdFrame(tk.Frame):
def __init__(self, parent, controller, *args, **kwargs):
super().__init__(*args, **kwargs)
self.controller = controller
self.parent = parent
label = tk.Label(self, text="Third Frame")
label.pack()
refresh_button = tk.Button(
self, text="Resfresh", command=self.parent.update_frame)
refresh_button.pack()
print(self.winfo_name())
if __name__ == "__main__":
app = RootFrame()
app.mainloop()
The code above is used to illustrate the problem. Please run the code and you'll see the changing widget name in the terminal.
I use winfo_parent and winfo_name in my actual code to create different conditions for button bindings. For example, if the user clicks a widget1 in frame6 happens X and when I click a widget8 in frame2 happens Y. This works until I destroy() and recreate something and everything breaks.
I suppose that using winfo_name and winfo_parent for this kind of referencing is not the correct way to get around, but I really can't think of anything else.
I'm not sure exactly what you are asking, but you can assign a specific name to the widget:
def create_third_frame(self):
self.third_frame = ThirdFrame(self, self.controller, name='testframe')
self.third_frame.pack()
Then each time the name of the frame created will be consistent.
You can also reference the widget by name with Tk().nametowidget(), see this relevant answer here: Is it possible to search widget by name in Tkinter?
>>> from Tkinter import *
>>> win = Tk()
>>> button = Button( Frame( win, name = "myframe" ), name = "mybutton" )
>>> win.nametowidget("myframe.mybutton")
<Tkinter.Button instance at 0x2550c68>
I would recommend sticking with a OOP approach however, and just reference it with from your code like self.thirdframes where you might have a list or dict of ThirdFrame objects. This way your python code can easily reference the objects without going back to the tcl interpreter and parse the widget name. If you ever will only have one ThirdFrame, then just reference back to self.thirdframe whenever you need it.
I am having issues with a Tkinter GUI. I need to create a large application. I need to use classes for that to manage all modules together. For a unit check and getting help here, I have tried to provide a sample which is close to exact problem ( with a small example) here:
I am creating a 1st window with a Button labelled as "Test". What I want is that when I click the button "Test", a new second window will pop up with a text "Enter Value" and entry space, where I can enter the value. I have provided the code below. What is happening is that, I am able to get the new window, but the text "Enter Value" and entry Space is generated in the first window instead of the second and the second window remains blank. I am not understanding where I am making the wrong logic call. Help will be very much appreciated.
I know we do not need classes for GUI applications, however to manage my large application ( not shown here), I need to have classes and I will very much appreciate, if some Tkinter Guru can help me with the bug in my code.
gui view File (gui_view.py)
import tkinter as tk
from tkinter import Tk
class MyMainGUI(tk.Frame):
def __init__(self, controller):
tk.Frame.__init__(self)
self.pack()
self.controller = controller
self.Button1=tk.Button(self)
self.Button1["text"]= "Test"
self.Button1["command"]=self.controller.buttonPressed1
self.Button1.grid(row=2,column=0,rowspan=2)
class MySecondGUI(tk.Frame):
def __init__(self, controller):
tk.Frame.__init__(self)
self.pack()
self.controller = controller
self.outputLabel2 = tk.Label(self)
self.outputLabel2["text"] = ("Enter Value")
self.outputLabel2.grid(row=1,rowspan=2)
#Entry Space
self.entrySpace2 = tk.Entry(self)
self.entrySpace2.grid(row=2,column=0,rowspan=2)
### gui Controller File (gui_controller.py)
import tkinter as tk
import gui_view # the VIEW file
class MainControl:
def __init__(self):
self.root = tk.Tk()
self.root.geometry('550x200')
self.view = gui_view.MyMainGUI(self)
self.view.mainloop()
def newWindow(self):
self.newWin = tk.Toplevel(self.root)
self.newWin.geometry('300x400')
self.newDisplay = tk.Label(self.newWin, text='Test Mode')
self.viewNew = gui_view.MySecondGUI(self.newWin)
self.viewNew.mainloop()
self.newDisplay.pack()
def buttonPressed1(self):
self.newWindow()
if __name__ == "__main__":
c = MainControl()
#
ADDING A MODIFIED CODE WITH NEW PROBLEM.
I have now been able to generate a code which pops up a new window with entries, when I click the button "Test" in the first Window. However, I am having problems creating buttons in the scond window. The way I have it now, it pops an error to me saying "'MySecondGUI' object has no attribute 'buttonPressed2"
Help will be very much appreciated.
I have pasted my updated code below:
GUI_VIEW FILE ( gui_view.py)
import tkinter as tk
from tkinter import Tk
class MyMainGUI(tk.Frame):
def __init__(self, controller):
tk.Frame.__init__(self)
self.pack()
self.controller = controller
self.Button1=tk.Button(self)
self.Button1["text"]= "Test"
self.Button1["command"]=self.controller.buttonPressed1
self.Button1.grid(row=2,column=0,rowspan=2)
class MySecondGUI(tk.Toplevel):
def __init__(self):
tk.Toplevel.__init__(self)
self.outputLabel2 = tk.Label(self)
self.outputLabel2["text"] = ("Enter Value")
self.outputLabel2.grid(row=5,rowspan=2)
self.entrySpace2 = tk.Entry(self)
self.entrySpace2.grid(row=8,column=0,rowspan=2)
self.Button2=tk.Button(self)
self.Button2["text"]= "Try Me"
self.Button2["command"] = self.buttonPressed2
self.Button2.grid(row=14,column=0,rowspan=2)enter code here
GUI MAIN CONTROLLER FILE
import tkinter as tk
import gui_view # the VIEW file
class MainControl:
def __init__(self):
self.root = tk.Tk()
self.root.geometry('550x200')
self.view = gui_view_temp.MyMainGUI(self)
self.view.mainloop()
def newWindow(self):
self.viewNew = gui_view.MySecondGUI()
self.viewNew.geometry('300x400')
self.newDisplay = tk.Label(self.newWin, text='Test Mode')
self.viewNew.mainloop()
self.newDisplay.pack()
def buttonPressed1(self):
self.newWindow()
def buttonPressed2(self):
pass
if name == "main":
c = MainControl()
in MySecondGUI(tk.Frame):
def __init__(self, controller):
#Attach your frame to "secondGUI" (which is your second window)
tk.Frame.__init__(self, controller)
in class MainControl:
#I assume you're passing your parent window/frame as "controller"?
self.viewNew = MySecondGUI(self.newWin)
according to Python Tkinter Docs
https://docs.python.org/3.5/library/tkinter.html#mapping-basic-tk-into-tkinter
You should specify your parent window if you're not attach your widget to main window.
#Main window
root = tk.Tk()
#Second Window
newWin = tk.Toplevel(root)
#Attach to main window
tk.Label(text="This label attached to root").pack()
tk.Button(text="This button attached to root").pack()
#Second Window
tk.Label(newWin, text="This label attached to second window").pack()
tk.Button(newWin, text="This button attached to second window").pack()
also,
self.viewNew.mainloop()
#This will fail because you have to set everything up before mainloop()
#_tkinter.TclError: can't invoke "pack" command: application has been destroyed
self.newDisplay.pack()
Edit for update
You should put your
def buttonPressed2(self):
in class MySecondGUI, not in class MainControl.
class MySecondGUI(tk.Toplevel):
def __init__(self):
tk.Toplevel.__init__(self)
self.outputLabel2 = tk.Label(self)
self.outputLabel2["text"] = ("Enter Value")
self.outputLabel2.grid(row=5,rowspan=2)
self.entrySpace2 = tk.Entry(self)
self.entrySpace2.grid(row=8,column=0,rowspan=2)
self.Button2=tk.Button(self)
self.Button2["text"]= "Try Me"
#self means "MySecondGUI" not "MainControl" here
self.Button2["command"] = self.buttonPressed2
self.Button2.grid(row=14,column=0,rowspan=2)
def buttonPressed2(self):
pass
I'm working on Windows XP, with Python 2.6.x and TKinter. Using a text widget in the app, but the standard popup menu (cut, copy, paste, delete, select all) is missing. How to make it appear?
I found a way, thanks to this post. I made some modifications. At the bottom part there's a minimal main to try it.
from Tkinter import *
def rClicker(e):
''' right click context menu for all Tk Entry and Text widgets
'''
try:
def rClick_Copy(e, apnd=0):
e.widget.event_generate('<Control-c>')
def rClick_Cut(e):
e.widget.event_generate('<Control-x>')
def rClick_Paste(e):
e.widget.event_generate('<Control-v>')
e.widget.focus()
nclst=[
(' Cut', lambda e=e: rClick_Cut(e)),
(' Copy', lambda e=e: rClick_Copy(e)),
(' Paste', lambda e=e: rClick_Paste(e)),
]
rmenu = Menu(None, tearoff=0, takefocus=0)
for (txt, cmd) in nclst:
rmenu.add_command(label=txt, command=cmd)
rmenu.tk_popup(e.x_root+40, e.y_root+10,entry="0")
except TclError:
print ' - rClick menu, something wrong'
pass
return "break"
def rClickbinder(r):
try:
for b in [ 'Text', 'Entry', 'Listbox', 'Label']: #
r.bind_class(b, sequence='<Button-3>',
func=rClicker, add='')
except TclError:
print ' - rClickbinder, something wrong'
pass
if __name__ == '__main__':
master = Tk()
ent = Entry(master, width=50)
ent.pack(anchor="w")
#bind context menu to a specific element
ent.bind('<Button-3>',rClicker, add='')
#or bind it to any Text/Entry/Listbox/Label element
#rClickbinder(master)
master.mainloop()
Thought I would share my solution, based on several code snippets on stackoverflow. It includes a minimum application to test:
edit: class bindings might not work. Should that be the case use normal bindings and return "break" in the select_all functions.
import Tkinter as tk
if 1: # nice widgets
import ttk
else:
ttk = tk
class EntryPlus(ttk.Entry):
def __init__(self, *args, **kwargs):
ttk.Entry.__init__(self, *args, **kwargs)
_rc_menu_install(self)
# overwrite default class binding so we don't need to return "break"
self.bind_class("Entry", "<Control-a>", self.event_select_all)
self.bind("<Button-3><ButtonRelease-3>", self.show_menu)
def event_select_all(self, *args):
self.focus_force()
self.selection_range(0, tk.END)
def show_menu(self, e):
self.tk.call("tk_popup", self.menu, e.x_root, e.y_root)
class TextPlus(tk.Text):
def __init__(self, *args, **kwargs):
tk.Text.__init__(self, *args, **kwargs)
_rc_menu_install(self)
# overwrite default class binding so we don't need to return "break"
self.bind_class("Text", "<Control-a>", self.event_select_all)
self.bind("<Button-3><ButtonRelease-3>", self.show_menu)
def event_select_all(self, *args):
self.focus_force()
self.tag_add("sel","1.0","end")
def show_menu(self, e):
self.tk.call("tk_popup", self.menu, e.x_root, e.y_root)
def _rc_menu_install(w):
w.menu = tk.Menu(w, tearoff=0)
w.menu.add_command(label="Cut")
w.menu.add_command(label="Copy")
w.menu.add_command(label="Paste")
w.menu.add_separator()
w.menu.add_command(label="Select all")
w.menu.entryconfigure("Cut", command=lambda: w.focus_force() or w.event_generate("<<Cut>>"))
w.menu.entryconfigure("Copy", command=lambda: w.focus_force() or w.event_generate("<<Copy>>"))
w.menu.entryconfigure("Paste", command=lambda: w.focus_force() or w.event_generate("<<Paste>>"))
w.menu.entryconfigure("Select all", command=w.event_select_all)
if __name__ == "__main__":
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.entry = EntryPlus(self)
self.text = TextPlus(self)
self.entry.pack()
self.text.pack()
self.entry.insert(0, "copy paste")
self.text.insert(tk.INSERT, "copy paste")
app = SampleApp()
app.mainloop()
Thanks to Gonzo's code and bluish '<Control-c>', my textwidget right button worked.
I don't have 50 reputations but I had a problem which I faced that the right button will trigger the function regardless of the location of the point clicked.
figured that out by:
def show_menu(self, e):
if e.widget==self.text:
self.tk.call("tk_popup", self.menu, e.x_root, e.y_root)
Now only the right button's popup will be triggered when you click on text widget.
To add function that deletes selected text in tkinter text widget, use:
def delete_highlighted_text():
text_widget.event_generate("<Delete>")