2

I am trying to make a entrybox that gets keyboard input when it is focused, and show only that key name in the middle of the box with no blinking bar and not allowing editing. And also changes a variable to the key.

So if [ ] is a entrybox. I type F7 then the box should show [ F7 ], then when I press the backspace button the box should show [ Backspace ].

In my code special keys likeF1, F2, ect are not even giving me the right feedback by giving me back '' for all function key and '\x08' for the backspace key. If all the keys showed different characters I think I could find a way to link the characters and the names I want to print. But this is not the case. And the entrybox types like this [1234| ] not like [ 1 ]. And I have no idea how to get the key names in the entrybox.

    def callback(event):
        key_input_entered.focus_set()
        print(repr(event.char))

    kb_frame = ttk.Frame(self.kb)
    kb_frame.grid(column=0, row=1, pady=(7, 19))
    ttk.Label(kb_frame, text='Enter Key').grid(column=0, row=0, pady=4)
    key_input = tk.StringVar()
    key_input_entered = ttk.Entry(kb_frame, width=15, textvariable=key_input)
    key_input_entered.grid(column=0, row=1)
    key_input_entered.bind('<Key>', callback)
Moses Kim
  • 242
  • 3
  • 16
  • I think you need to create a keyboard eventhandler and use it to populate `Text` widget or something similar. – martineau May 04 '19 at 07:27
  • Following the information [here](http://effbot.org/tkinterbook/entry.htm) I think you need to use `justify=CENTER` (or maybe it's 'center' idk) in order to center your text within your entry box – Reedinationer May 04 '19 at 07:28

1 Answers1

2

To get all the features in an Entry widget you need to modify it.

  1. Unbind the sequence <Key> and also the <BackSpace> from the Entry widget.

  2. Justify the text to align in center by configure justify='center'.

  3. To get the desired key name, you have to bind <Key> to the Entry widget and get event.keysym as it gives you the name of the key pressed.

  4. If you don't want to see the insert blinking in the Entry widget you can try insertwidth=0 but for me it doesn't work not sure why, so I switch between 'readonly' and 'normal' states just like in the function self._display(..) as when the Entry widget is on 'readonly' state it doesn't allow any text inserts.

Here is the custom class Entry_Box inherited from Entry widget.

import tkinter as tk

class EntryBox(tk.Entry):
    def __init__(self, master=None, cnf={}, **kw):
        kw = tk._cnfmerge( (kw, cnf) )
        kw['justify'] = kw.get('justify', 'center')
        kw['state'] = 'readonly' 
        super(EntryBox, self).__init__(master=master, **kw)
        self.bind_class(self, '<Key>', self._display)

    def _display(self, evt):
        self['state'] = 'normal'
        self.delete('0', 'end')
        self.insert('0', str(evt.keysym))
        self['state'] = 'readonly'


if __name__ == "__main__":
    root = tk.Tk()
    EntryBox().pack()
    root.mainloop()

Brief explanation of the code:

tk._cnfmerge() is an internal function of tkinter library the purpose of this function is to combine multiple dictionaries together. Now you might be wondering we can combine dictionaries without this function. Yes, we can but this way we won't get any errors like if any of the dictionaries is None. Here is the source code to the function.

The bind_class is just like bind function but it is referred to the internal class name for example Entry widget has binds like <Key>, <BackSpace>, <Return>, ... which are internal binds, so if a user tries to bind or unbind any sequence it won't interfere with the internal binds until they use unbind_class with the same className (className is like a tag) given internally. This post can explain better.

Saad
  • 3,340
  • 2
  • 10
  • 32
  • This works just like I wanted it to do. But I cannot understand the code except for ```_display function```. I don't know what ```tk.Enter``` is doing inside ```EntryBox(): ``` and cannot find information for ```tk._cnfmerge``` and can't figure out what ```Entry``` in ```self.unbind_class``` is pointing at. Can you show me an example for this inside a toplevel window? – Moses Kim May 04 '19 at 15:31
  • I managed to get the entrybox inside the toplevel window. But I still don't know how the thing works. – Moses Kim May 04 '19 at 15:59
  • I updated my post with some important information and links so you can have a better understanding on how things work. – Saad May 04 '19 at 16:03
  • I don't know if I understood it right. Does it mean that ```self.unbind_class('Entry', '')``` let me use the bindtag from ```tk.Enter```? Then why ```self.bind_class(self, '', self._display)``` have ```self``` in it's classname when ```class Entrybox``` can use bindtag `````` ? And I commented the two ```self.unbind_class``` and it works the same. What do they do to the program? And this may be stupid but I still don't know why this `class makes an entrybox with just ```tk.Enter``` inside ```()``` of```class EntryBox():```. – Moses Kim May 04 '19 at 16:30
  • And one more, right ctrl and right alt key doesn't seen to work properly. It shows both shows ```???```. How can I fix this? – Moses Kim May 04 '19 at 16:33
  • I unbind the internal class sequence `` so it won't type by mistake probably `self.unbind_class('Entry', '')` is not so important but `self.unbind_class('Entry', '')` is important if you won't unbind `` then you can't see backspace key when pressed. Basically I just unbind the default binds of the `Entry` widget class. I gave `self` as a className – Saad May 04 '19 at 16:38
  • You're right, you probably don't need those as the binds are overrides. I used those to unbind the internal binds of the `Entry` widget class so it won't interfere. And as for just getting left alt or ctrl keys, I don't know how to fix that but I'll try to and let you know. – Saad May 04 '19 at 16:48
  • If it's ok with you, can I ask some more? I have added some code on a new answer using your method. How can I manage to get ```evt.keysym``` from my toplevel window to my main window. I have a listbox inside my main window and I am wishing to get the key inside the entrybox to be enlisted in the listbox when I press confirm. – Moses Kim May 04 '19 at 16:58
  • It is nice to ask follow up questions but I'm sorry to say this is not the right way. You can't edit your post and change it to something different, to ask a follow up question you have to post again and link back to the original post so people can see the context. [What is the the best way to ask follow up questions?](https://meta.stackoverflow.com/questions/266767/what-is-the-the-best-way-to-ask-follow-up-questions) – Saad May 04 '19 at 17:06
  • Oh I'll stick to that from now. I'll add a comment of the new question. Thank you so much for you help. – Moses Kim May 04 '19 at 17:17
  • https://stackoverflow.com/questions/55985084/how-to-get-variables-from-toplevel-window-to-main-window this is the link to the question. – Moses Kim May 04 '19 at 17:25
  • Can this be modified to show multiple keys? Like if you press a radiobutton from 'single input' to 'multi input' and type ```ctrl``` and ```c``` it shows ```[ crtl + c ]``` – Moses Kim May 04 '19 at 19:11
  • Yes, it is possible but I recommend you to try it first and if then you need help, we will help. Also it would be very helpful if you accept the answers if they helped you :) – Saad May 04 '19 at 19:28
  • I accepted you answer. And I'll try my best and come back when I'm stuck again. :) – Moses Kim May 04 '19 at 19:33