Tkinter international bind
Asked Answered
A

5

7

Is there a way in Tkinter to bind a combination of keys that will work in all keyboard layouts? (bind by scancode)

For example, I need 'Control-Z' binding that will work with the same physical key in the lower left corner of the keyboard in all layouts, such as:
    * Russian layout,
    * Greek layout, etc.

Here's what I tried:

from Tkinter import *
root=Tk()
def f(event):
    print 'undo'
button1=Button(root, text=u'Button')
button1.pack()
button1.bind('<Control-z>', f)
root.mainloop()

It doesn't work for Russian and Greek keyboard layouts.

Update-2:

I did some more experiments with Windows and now the general rule is like that:

    1) If the language is based on latin character set, keys are mapped "by value" (German, French, Dvorak) so that the same action is mapped to different physical keys.
    2) If it is not (eg Russian, Greek), then all major accelerators are mapped "by position" (to match the corresponding English letter usually marked on the same key).

Only the second case needs special attention. Any ideas if this is implemented in some lib already?

Update-3

It is simply reproduced even without Russian keyboard or Russian Windows.

1) Start->Control Panel->Regional and Language Options
2) Language->Details
3) Add Russian language.

That's it. Now Alt-Shift will switch you to Russian and you'll be able to type the following funny symbols:

Russian keyboard

another Alt-Shift will switch you back.

Forget what Wikipedia says about phonetic Russian layouts. They are not used these days. At least inside Russia.

All Windows applications (including wxPython ones) use Ctrl-я for undo, Ctrl-ч for cut, Ctrl-с for copy and so on.

Asteriated answered 22/1, 2013 at 9:48 Comment(9)
This might be relevant.Swain
Yes, thanks. I looked through the conversation before asking - no clues so far. Updated the question.Asteriated
BTW in spite of the "consensus" they "apparently have" it's a shame that Russian accelerators don't work in IDLE. They work in every single application in Windows except IDLE and for me it was one of the reasons why I don't use IDLE at all. Not to mention that it deters newbies from learning python.Asteriated
@AntonyHatchkins first you have to solve this in Tk, then you can start caring about IDLE in specific. Do you have a solution for Tk that works for all the keyboard layouts ?Pinhead
I guess it should be an design decision, not just a simple patch. I didn't look deeper than Tkinter lib. The workaround described in my answer below is neither elegant nor universal, but it works for me. My next project will hopefully be wxPython-powered. Maybe someone who knows Tk (I don't) will solve it one day.Asteriated
@AntonyHatchkins you could help it by posting the bug on the relevant bugtracker, although it is likely that it has been reported already. Even if that is the case, then you will know it has been reported, and maybe you can learn why it is not fixed yet. The workaround you provided clearly doesn't solve anything, because now it doesn't work for me.Pinhead
It's really strange it doesn't. I tested with Windows XP, Windows Server 2003, Windows 7 Prof. What it should do is print 'Ctrl-C' whenever 'Ctrl-C' is pressed in Russian layout (not in English layout though, that needs separate binding)Asteriated
Here's a wiki page with related discussion.Asteriated
Try binding <Key> event and check whether Ctrl key (event.state & 4 > 0) and z (event.keycode == 90) are pressed in the event handler.Wind
S
2

Thanks to @acw1668 for help!

You need to do something like this to use hotkeys with any language layout (the callback from this example is running when Control key is pressed, and prints the key that is pressed in the same time with Control:

from tkinter import *


def callback(event):
    if (event.state & 4 > 0):
        print("Ctrl-%s pressed" % chr(event.keycode))

root = Tk()
root.bind("<Key>", callback)
root.mainloop()

PS: This example was checked on Windows 10 when English, Ukrainian, Russian, Arabic, Amharic, Armenian, Greek, Georgian, French, Chinese, Japanese and other language layouts were used.

Scrimmage answered 12/12, 2019 at 16:41 Comment(4)
event.keysym == "??" can be used to check if layout is not english https://mcmap.net/q/1626328/-tkinter-determine-keyboard-layoutCatlett
@Catlett that's great but this doesn't have anything to do with my answer as well as the OP's question.Scrimmage
I mean, this can be used for hotkeys which need processing only for non-english layouts. e.g. Ctrl+V pastes text if your current layout is english, but needs manual processing for other layouts.Catlett
@Catlett the question is how to make hotkeys behave identically regardless of keyboard layout. And you are changing their behavior for non-English layouts.Scrimmage
A
1

What I'm primarily interested in is Russian layout in Windows.

The quick and dirty workaround I currently use is:

import Tkinter

def copy(event):
    print 'Ctrl-C'

root = Tkinter.Tk()
root.bind('<Control-ntilde>', copy)
root.mainloop()

which could potentially lead to a conflict with <Ctrl + actual ntilde> in some other language.

It could be overcome if I could determine which layout is currently active, thus second question: Tkinter determine keyboard layout.

Another drawback is that due to 'universal' treatment of modifier keys it also fires when I press Ctrl-Alt-V, but that's another story as it applies to English layout as well.

Asteriated answered 23/1, 2013 at 8:4 Comment(1)
The snippet from Junuxx link has a solution to the latter problem: event.state is different for Ctrl-Alt-V and Ctrl-V.Asteriated
K
1

I have a partial and rather ugly solution for this. In the code below I have a window with Text widget, which have some "in-box" connection between standard Ctrl+C keyboard events and their proper handling. However, if I simply change the keyboard layout to, say, Russian, these functions do not work anymore. To solve the problem I re-wrote implementation for these events, and now everything works fine. But I feel slightly frustrated about such a solution. Does anyone have any better ideas?.. For instance, is there a way to trigger (or mimic) "normal" key press in python tkinter?

import tkinter

root = tkinter.Tk()

class MyWind (tkinter.Frame):
    def __init__(self, parent):
        tkinter.Frame.__init__(self, parent)
        self.create_UI()

    def create_UI(self):
        text_field = tkinter.Text(self)
        text_field.insert(tkinter.END, "Hello world")
        text_field.pack()

def print_event(event):
    print ("Event character code <char>: '%s'" % event.char)
    print ("   Event key symbol <keysym>: '%s'" % event.keysym)
    print ("   Event key code <keycode>: '%s'" % event.keycode)

def work_out_event(event): # Here is the solution
    widget_type = type(event.widget)
    if widget_type == tkinter.Text:
        content = event.widget.selection_get()
        print ("Selected content = '%s'" % content)
        root.clipboard_clear() 
        root.clipboard_append(content)

def lurker1(event):
    print ("Crtl + C (english) pressed!")       
    print_event(event)

def lurker2(event):
    print ("Crtl + C (russian) pressed!")
    print_event(event)
    work_out_event(event)        

root.bind("<Control-c>", lurker1)      # "C" character with the english keyboard layout
root.bind("<Control-ntilde>", lurker2) # "C" character with the russian keyboard layout

root.app = MyWind(root)
root.app.pack()
root.mainloop()
Kilmarx answered 20/8, 2014 at 19:6 Comment(2)
What is the difference between your answer and this ("the quick and dirty workaround I currently use") one?Asteriated
No principle difference, I just wanted to give code providing more info on how copy operation to buffer can actually be handled. The main problem is the same as yours: I don't want to re-write the standard functionality of Ctrl+C and would be much more happier to pass this to some internal event handler.Kilmarx
A
0

Another option already suggested in the old 1999 is to switch from Tkinter to wxPython where accelerators handling is done for all types of keyboard layouts automatically (eg Editor example here: http://wiki.wxpython.org/AnotherTutorial).

Asteriated answered 23/1, 2013 at 9:16 Comment(0)
C
0
def copy(event):
    print 'Ctrl-C'
    master.clipboard_append('text')

and it works!

Cardwell answered 6/12, 2014 at 11:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.