How can I disable the copying and pasting on the tkinter entry field. I tried using:
self.ent_city = Tkinter.Entry(bd='4',width='32', state='readonly')
But this command is nearly similar to state = 'disabled' . I want to disable Copy/paste or Cut/Paste on my entry widget.
Any help would be appreciated.!
Thanks in advance
You can bind key-presses and the right-button click like this:
from Tkinter import *
class App(Frame):
def __init__(self):
Frame.__init__(self)
self.pack()
self.ent = Entry(self, width=15)
self.ent.pack()
self.ent.bind('<Control-x>', lambda e: 'break') #disable cut
self.ent.bind('<Control-c>', lambda e: 'break') #disable copy
self.ent.bind('<Control-v>', lambda e: 'break') #disable paste
self.ent.bind('<Button-3>', lambda e: 'break') #disable right-click
root = Tk()
app = App()
mainloop()
It's not fool-proof, but I think it's a decent solution. Check http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm for more info.
I don't think there's an official way to disable cut/copy/paste in tkinter (very annoying), and calling self.ent.bind('<Control-v>', lambda e:'break') doesn't seem to work on XWayland (tkinter runs in x11 on Xorg, or XWayland on Wayland)
I've tried self.ent.unbind('<<paste>>') but this is no help either.
A bodge that seems to work is to bind your own function to paste, in it, empty the clipboard into a string, then fill it again after a 20ms timeout, this way, when tkinter pastes, it pastes nothing. This isn't ideal though as any image or html on the clipboard will get mushed up. Also, you have to catch the error if the clipboard is empty.
self.ent.bind('<Control-v>', self.paste)
def paste(self, event):
try: s = self.ent.clipboard_get()
except: s = ''
self.ent.clipboard_clear()
#any other code you want to run on paste here
self.ent.after(20, lambda: self.ent.clipboard_append(s))
Related
I have found a behaviour which seems to be a bug in tkinter.
If you run the following (minimal to reproduce the bug) code:
import tkinter, tkinter.simpledialog, tkinter.scrolledtext
root = tkinter.Tk('test')
text = tkinter.scrolledtext.ScrolledText(master=root, wrap='none')
text.pack(side="top", fill="both", expand=True, padx=0, pady=0)
text.insert(tkinter.END, 'abc\ndef\nghi\nijk')
root.mainloop()
then:
select one row in the scrolledtext widget, e.g. the row "ghi",
copy it with CTRL+C
do nothing else and close the app
Then paste it (CTRL+V) in any other Windows app: it won't work, nothing will be pasted. Why?
How to solve this?
Note: the expected behaviour is that text copied with CTRL+C should persist in the clipboard even if the app is closed. This is the default behaviour in many Windows software. Example here with notepad.exe:
link to the animated screen capture: https://i.imgur.com/li7UvYw.mp4
Note: this is linked to
https://bugs.python.org/issue23760
Tkinter in Python 3.4 on Windows don't post internal clipboard data to the Windows clipboard on exit
Tk only copies to clipboard if "paste" is used before program exits
https://core.tcl-lang.org/tk/tktview/1844034fffffffffffff
etc.
You can also use pyperclip which supports Windows, Linux and Mac
import tkinter as tk
import pyperclip
def copy(event:tk.Event=None) -> str:
try:
text = text_widget.selection_get()
pyperclip.copy(text)
except tk.TclError:
pass
return "break"
root = tk.Tk()
text_widget = tk.Text(root)
text_widget.pack()
text_widget.bind("<Control-c>", copy)
root.mainloop()
For a Windows only solution try this:
import tkinter as tk
import os
def copy(event:tk.Event=None) -> str:
try:
# Get the selected text
# Taken from: https://stackoverflow.com/a/4073612/11106801
text = text_widget.selection_get()
# Copy the text
# Inspired from: https://codegolf.stackexchange.com/a/111405
os.system("echo.%s|clip" % text)
print(f"{text!r} is in the clipboard")
# No selection was made:
except tk.TclError:
pass
# Stop tkinter's built in copy:
return "break"
root = tk.Tk()
text_widget = tk.Text(root)
text_widget.pack()
text_widget.bind("<Control-c>", copy)
root.mainloop()
Basically I call my own copy function whenever the user presses Control-C. In that function I use the clip.exe program that is part of the OS to copy the text.
Note: my approach to copying data to the clipboard using os.system, isn't great as you can't copy | characters. I recommend looking here for better ways. You just need to replace that 1 line of code.
Using pyperclip and root.bind_all() we can solve the problem.
import tkinter, tkinter.simpledialog, tkinter.scrolledtext
import pyperclip as clip
root = tkinter.Tk('test')
text = tkinter.scrolledtext.ScrolledText(master=root, wrap='none')
def _copy(event):
try:
string = text.selection_get()
clip.copy(string)
except:pass
root.bind_all("<Control-c>",_copy)
text.pack(side="top", fill="both", expand=True, padx=0, pady=0)
text.insert(tkinter.END,'abc\ndef\nghi\njkl')
root.mainloop()
I am trying to get a Tkinter popup to display when a button is clicked.My issue is that every thing runs just fine except the popup will not produce. I have tried multiple ways to create the popup using tkMessagebox and Toplevel() but still not luck. The program runs but when the button is click nothing happens. I have referenced similar post but still can not find the issue in my code. Any thoughts?
from tkinter import *
def new():
root2 = Tk()
root2.geometry('250x250')
l = Label(root2,text="Please Scan Tag").pack()
root2.mainloop()
# setting main frame
root = Tk()
root.geometry('800x650')
root.title("Pass")
root.configure(background= "white")
label_0 = Label(root, text="Pass",width=10,font=("bold", 50),fg= "green",bg="white")
label_0.place(x=186,y=76)
Button(root,command="new", text='new',font=
("bold",15),width=15,height=4,bg='blue',fg='white').place(x=155,y=300)
root.mainloop()
The command option requires a reference to a callable function, not a string.
Button(root,command=new, ...)
I'm using tkinter's "askokcancel" message box to warn the user, with a pop-up, of an irreversible action.
from tkinter import Tk
Tk().withdraw()
from tkinter.messagebox import askokcancel
askokcancel("Warning", "This will delete stuff")
I'd like to change the text of the 'OK' button (from 'OK') to something like 'Delete', to make it less benign-looking.
Is this possible?
If not, what is another way to achieve it? Preferably without introducing any dependancies...
Why not open a child window thus creating your own box with your own button like this:
from tkinter import *
def messageWindow():
win = Toplevel()
win.title('warning')
message = "This will delete stuff"
Label(win, text=message).pack()
Button(win, text='Delete', command=win.destroy).pack()
root = Tk()
Button(root, text='Bring up Message', command=messageWindow).pack()
root.mainloop()
No, there is no way to change the text of the buttons for the built-in dialogs.
Your best option is to create your own dialog. It's not very hard to do, and it gives you absolute control over what is in the dialog widget.
It is true that you cannot change names in Tkinter messageboxes, but you can make your own class of messagebox so that you can reuse it several times. The following code is the same as Tkinter messagebox, but it has args and kwargs as arguments. It is just for convenience.
I am also learning, so the code does not have flash and alarm.
class Message(object):
def __init__(self,parent, *args, **kwargs):
self.parent=parent
top=Toplevel(self.parent)
top.geometry("400x200")
top.transient(self.parent)
top.title(args[0])
f0=Frame(top)
top.l1=Label(f0, text=args[1])
top.l1.pack(pady=30)
f0.pack()
top.grab_set()
top.f1=Frame(top)
for key in kwargs:
key=Button(top.f1, text=f"{kwargs[key]}")
key.pack(side=LEFT)
top.f1.pack()
The message box will look like this:
So I have this code:
try:
# for Python2
from Tkinter import *
except ImportError:
# for Python3
from tkinter import *
class Injector():
def __openInjector(self):
root = Tk()
root.geometry('600x400')
root.title('Toontown Rewritten Injector')
root.resizable(False, False)
def __init__(self):
self.code = ''
self.__openInjector()
def runInjectorCode(self):
exec(self.code.get(1.0, 'end'), globals())
def __openInjector(self):
root = Tk()
root.geometry('600x400')
root.title('Toontown Rewritten Injector')
root.resizable(False, False)
frame = Frame(root)
self.code = Text(frame, width=70, height=20)
self.code.pack(side='left')
Button(root, text='Inject!', command=self.runInjectorCode).pack()
scroll = Scrollbar(frame)
scroll.pack(fill='y', side='right')
scroll.config(command=self.code.yview)
self.code.config(yscrollcommand=scroll.set)
frame.pack(fill='y')
Injector()
In the IDLE console it works fine and does everthing I want it to do. But whenever I run the .py file on my Desktop. The black window appears, then just closes and nothing happens. Any help?
First, you have two methods in your class with the same name. The first one gets overwritten by the second one. At the end of that second one, you need the following line:
root.mainloop()
This will actually run the GUI. It's needed when running from a script, but not when running within the interactive interpreter.
Add it at the end of the second __openInjector:
...
self.code.config(yscrollcommand=scroll.set)
frame.pack(fill='y')
root.mainloop()
At the end of your second __openInjector method, add the line: root.mainloop().
This is necessary for Tkinter to run your code. mainloop is really nothing more than an infinite loop that waits for events. An event may be a user interaction, such as clicking a button.
My guess is you don't need mainloop when running interactively for purely convenience reasons.
I have a ComboBox written in Python Tkinter that makes the horrible system alert sound when you click off of it without selecting something.
For instance, when you hit the dropdown and select your item, it works fine. But if you hit the drop-down and then decide to click off, it will lose focus as expected, but it makes an alert sound. Can this be disabled in some way so it can gracefully lose focus without complaining? I'm on OSX 10.9 btw
UPDATE -
Minimally working code that produces the alert.
from Tkconstants import *
import ttk
import Tkinter
class PyPrecursor():
def __init__(self,root):
self.root = root
self.TabNotebook()
def TabNotebook(self):
self.main_notebook_frame = ttk.Notebook(self.root, name='main_notebook')
self.main_notebook_frame.enable_traversal()
self.OptionsF = ttk.Frame(self.main_notebook_frame, name='options')
self.length_options_frame = ttk.LabelFrame(
self.OptionsF, labelwidget=ttk.Label(font=('Arial', 15), text="Length Options:"))
self.hcdr3_length_label = ttk.Label(self.length_options_frame, text="HCDR3 Length")
self.HCDR3_Length = Tkinter.StringVar()
self.hcdr3_length_combo = ttk.Combobox(
self.length_options_frame, values=[i for i in xrange(16, 36)],
textvariable=self.HCDR3_Length)
self.hcdr3_length_combo.current(0)
self.length_options_frame.pack(side=TOP,fill=X,pady=5)
self.hcdr3_length_label.pack(side=LEFT)
self.hcdr3_length_combo.pack(side=LEFT,anchor=W)
self.main_notebook_frame.pack(side=TOP,expand=1,fill=BOTH,padx=10,pady=10)
self.main_notebook_frame.add(
self.OptionsF, text="Input Options", underline=0, padding=2)
self.main_notebook_frame.bind("<<NotebookTabChanged>>",self.update_)
def update_(self,event):
self.root.update()
def main():
root = Tkinter.Tk()
PyPrecursor(root)
root.mainloop()
root.update_idletasks()
if __name__ == '__main__':
main()
You might want to try this:
self.hcdr3_length_combo.bell(displayof=1)
Not sure if it should be 1 or 0 though...
If it doesn't work, maybe a containing widget throws the sound. Might want to apply it to the parent widget as well. I'm not familiar with python 2.7 and it doesn't throw a sound when I use python3 with slight modifications.
Usually when you can't find an option for a specific widget, you can find something in the general widget options. Just search "tkinter widget options" and you'll get some place like:
https://effbot.org/tkinterbook/widget.htm