binding keyboard events tkinter - python-3.x

Still quite new at python , i'm learning tkinter.
I would like to use keyboard events rather than mouse events for some fonction.
However keyboard events do not work although mouse events do.
here is a very simple example of what is not working. Using button 1 with the mouse and pressing key 'z on the keyboard should do the same, but the keyboard does nothing. I have tried to read tkinter documentation but didn't find the answer.
thanks for your help
from tkinter import *
class Pipi(Frame):
def __init__(self,master=None):
Frame.__init__(self,width=400,height=400,bg='red')
self.pack()
self.bind("<z>",self.do)
self.bind("<Button-1>", self.do)
def do(self,event):
print('la vie est belle')
root=Tk()
Pipi(root)
root.mainloop()

This is due to Frame widget not having the focus. With the mouse event, it works seemingly different. There can be a couple of workarounds:
Grabbing the focus before the event happens
binding to something else
To achieve the first, simply add:
self.focus_set()
somewhere inside __init__.
To achieve the second, replace:
self.bind("<z>",self.do)
with:
self.master.bind('<z>', self.do)

Related

Fixing the place of a Tkinter window (not a widget)

This is the code for the window:
import tkinter as tk
root = tk.Tk()
root.resizable(False, False)
root.geometry('200x200+400+400')
This window can be dragged to move from its place. How to to fix it in its place?
Do you mean that the window can be a non-fullscrean size, but it can't be dragged around like another application? I think that is on the operating system side of the problem. What are you making? Maybe there is a workaround.
It is possible however to make it forced fullscreen. For that Check here: https://www.tutorialspoint.com/how-to-display-full-screen-mode-on-tkinter#:~:text=Tkinter%20displays%20the%20application%20window,fullscreen%2C%20toolwindow%2C%20and%20topmost.

pyqt5 permanent widget focus

in my pyqt5 application, I have a QLineEdit that can have its value changed by both typing and pressing buttons that have advanced actions on the QLineEdit's text.
I have done some research and found the setFocusPolicy, but it does not have a permanent focus option, is there a way around this?
edit: by "permanent focus" I mean that the QLineEdit is always in a focus state where one can type into it, but clicking somewhere else or tabbing does not change the focus to a different widget
musicamante had it right, what I did was add a corresponding *widgetObject*.setFocusPolicy(Qt.NoFocus) line for each of my widgets that could possibly focus
note: I had this import line specifically for the NoFocus constant
from PyQt5.QtCore import Qt

tkinter menu accelerator duplicating callbacks

I'm seeing some strange behavior running Python 3.7.6 and tkinter 8.6 on MacOS 10.14.6.
I've bound "Delete" to a function, and also added a menu command that triggers the same function. I've also added an accelerator option to the menu command, which only displays the text and doesn't actually create the shortcut (as noted here).
Except, in this case, it does seem to create a shortcut: pressing the "Delete" key triggers my function twice: once for the bound command, and a second time from the accelerator. Removing either the binding or the accelerator cause it to only trigger once; of course, removing both causes the keybind to no longer activate. It works fine when triggered from the menu, and this doesn't seem to happen with other keys or key combos.
I'm not sure whether I've missed a detail about Tkinter programming or I'm encountering a technical issue. I made sure to try a different keyboard, and am unsure whether it might be a bug with Tkinter on Mac.
The following code consistently exhibits my issue:
from tkinter import *
def bar(event=None):
print("Called bar")
def foo(event=None):
print("Called foo")
root = Tk()
menubar = Menu(root)
filemenu = Menu(menubar, tearoff=0)
#works fine
filemenu.add_command(label="Something else", command=bar, accelerator="Control-s")
root.bind("<Control-s>", bar)
#triggers twice when using the "Del" key
filemenu.add_command(label="Delete a thing", command=foo, accelerator="Delete")
root.bind("<Delete>", foo)
menubar.add_cascade(label="File", menu=filemenu)
root.config(menu=menubar)
root.mainloop()
Triggering each shortcut once generates the following output:
Called bar
Called foo
Called foo
So this leaves me with two questions:
Can this be replicated by other people?
How might I resolve or work around it?
I also had this issue, and looking at the code it appears that it definitely does add bindings for your accelerators. In my app I worked around it by not binding the key presses on OSX but it's hardly ideal..
There is a Tk bug for this: https://core.tcl-lang.org/tk/tktedit?name=ead70921a9665f57d2fbcfb3cdee51391858bc96
Your code worked fine on my windows system running Python 3.7, but believing there are some issues with tkinter on MacOS, you can try the using the themed tk ttk Menubutton widget and see if it works any better. The look of the ttk Menu might slightly differ from the tk Menu design.
Here is your posted code with the ttk Menubutton:
import tkinter as tk
from tkinter import ttk
def bar(event=None):
print("Called bar")
def foo(event=None):
print("Called foo")
root = tk.Tk()
# create a ttk menubutton
mb = ttk.Menubutton(root, text="File")
# create a cascade of menu options for this menubutton mb
mb.menu = tk.Menu(mb, tearoff=0)
mb.menu.add_command(label="Something else", command=bar, accelerator="Control-s")
root.bind("<Control-s>", bar)
mb.menu.add_command(label="Delete a thing",command=foo, accelerator="Delete")
root.bind("<Delete>", foo)
mb.configure(menu=mb.menu)
mb.pack(side='left')
root.mainloop()
I hope it works!

Closing a tkinter Entry Widget without killing the program

I am currently stepping through the tkinter tutorial at python-course.eu. Is it possible to close an entry widget without killing the program? What I am trying to do is incorporate the tkinter entry box into a pygame program such that the program asks for the players name via an entry box and then closes once the text has been entered. The game should then continue. Is this possible?
What I would like to do is:
-create a pygame surface
-open a tkinter entry widget on top of that surface
-get the users name
-close the tkinter widget
-write text using pygame onto the surface that incorporates the user's name
What is stumping me is the fact that the tkinter examples on python-course.eu all end with a mainloop() statement while I would like pygame to have an event loop so that I can expand the program. I anticipate that the widget creation would occur prior to dropping into the event loop. This is where I am stuck :-(
rather than trying to mix two GUI libraries in the same window, if a prompt is acceptable you could use:
import tkinter as tk
from tkinter import simpledialog
root = tk.Tk() # needed to prevent extra window being created by dialog
root.withdraw() # hide window as not needed
username = simpledialog.askstring('Username', 'Enter username:')
root.destroy()

Python Tkinter Clickable Text?

I'm wondering if there's a way to make clickable text in Tkinter. Maybe like you would see on a title screen of a game, and where you hover your mouse over the text and it changes color/hightlights itself. All I need the click to do is execute another function.
Are either of these things possible? Thanks!
you are looking for tkinter's events:
tk_widget.bind("<Button-1>",CALLBACK)
The call back needs to take an event argument which is a dictionary containing information about what triggered the event.
This can run into issues with widgets that overlap such as windows in a canvas or labels sometimes triggering the callback for the window behind it.
For hovering the mouse over a widget the event is called "<Enter>" and moving mouse out of widget region is called "<Leave>" for highlighting text effect, if you just want to capture a click anywhere on a window then on the root call root.bind_all("<Button-1>",CALLBACK)
source: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/index.html
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/events.html
example:
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
def change_case(event=None):
new_text = str.swapcase(lab["text"])
lab.config(text=new_text)
def red_text(event=None):
lab.config(fg="red")
def black_text(event=None):
lab.config(fg="black")
root = tk.Tk()
lab = tk.Label(root,text="this is a test")
lab.bind("<Button-1>",change_case)
lab.bind("<Enter>",red_text)
lab.bind("<Leave>",black_text)
lab.grid()
root.mainloop()
hope this helps :)

Resources