I am writing a small tkinter-app involving user keyboard-input, and want to control the range of acceptable input-characters. I'm currently using an Entry-widget, but haven't been able to access the individual characters AS THEY ARE ENTERED. What I (think I:confused:) need is somthing like the 'command=' option (as in the Button-widget), which would call a function at each keyboard-entry. Can anyone point me in the right direction? Thanks.

Recommended Answers

All 11 Replies

This should help you ...

# bind and show a key event with Tkinter

from Tkinter import *

root = Tk()
prompt = '      Press any key      '
label1 = Label(root, text=prompt, width=len(prompt))
label1.pack()

def key(event):
    if event.char == event.keysym:
        msg = 'Normal Key %r' % event.char
    elif len(event.char) == 1:
        msg = 'Punctuation Key %r (%r)' % (event.keysym, event.char)
    else:
        msg = 'Special Key %r' % event.keysym
    label1.config(text=msg)

root.bind_all('<Key>', key)

root.mainloop()

vegaseat, many, MANY thanks; from the look of your snippet it seems to be exactly what I need. Cheers.

I imbedded vegaseat's code into my app - see below - and it works (of course), except for the final line:

def key(self, event):
        if event.char == event.keysym:
          msg = 'Normal Key %r' % event.char
        elif len(event.char) == 1:
          msg = 'Punctuation Key %r (%r)' % (event.keysym, event.char)
        else:
          msg = 'Special Key %r' % event.keysym
        self.l00.config(text=msg)

When I enter something, I get the message:

AttributeError: Application instance has no attribute 'l00'

With "self." removed, the message is:

NameError: global name 'l00' is not defined

I imagine that I must have a scope problem, which I have spent two days trying to solve, but to no avail. l00, BTW, is a label-widget, which was instantiated using the same class:

self.l00 = Label(self, borderwidth=1, relief=RIDGE, anchor=W)
self.l00.grid(row=0, column=0, columnspan=3)

It may be a simple solution, but I can't see it; can someone help me out again?

And another quick point; is it possible to give widgets a sort of GLOBAL attribute, to make them generally accessable within an app.?

Thanks.

Can you give us the whole class?

Just a note, 'l00' is a heck of a variable name, looks a lot like the number '100', I would avoid it!

Can you give us the whole class?

Just a note, 'l00' is a heck of a variable name, looks a lot like the number '100', I would avoid it!

Thanks; here it is, hope you can figure out my beginner's scribblings;)

class Application(LabelFrame):              # 3
    def __init__(self, rowpos, colpos, framenr, crWidg, master=None):
        LabelFrame.__init__(self, master)#, name='lf'+chr(framenr+ord('0')))  # 4
    if crWidg == 1: 
          self.grid(row=rowpos, column=colpos)                    # 5
      self.createWidgets(framenr, rowpos, colpos)
    elif crWidg == 0:
          self.grid(row=rowpos, column=colpos, columnspan=3, sticky=W)                    # 5
      lident = 'l'+chr(rowpos+ord('0'))+chr(colpos+ord('0'))
          self.l00 = Label(self, borderwidth=1, relief=RIDGE, anchor=W,        \
                           name=lident, text="                 "+lident+"             ")
          self.l00.grid(row=0, column=0, columnspan=3)
    else:
          self.grid(row=rowpos, column=0, columnspan=3, sticky=E)                    # 5
          self.e01 = Entry(self, borderwidth=0, relief=RIDGE, width=colpos)
          self.e01.grid(row=rowpos, sticky=E)
      self.e01.bind_all('<Key>', self.key)
      self.e01.focus_set()

#
# this function generates a widget (cell) name relating to its position within the LabelFrame
#    
    def get_cell_ident(self, framenr, cells_per_frame_inX, cells_per_frame_inY, rownum, colnum):
      rowst, colst = divmod(framenr,cells_per_frame_inX)            # get result and remainder
      rowst = rowst*cells_per_frame_inY                             # start-nr of the row-cell
      colst = colst*cells_per_frame_inX                             # start-nr of the column-cell
      text = chr(rowst+rownum+ord('0'))+ \
             chr(colst+colnum+ord('0'))                             # this is the ident described in the top table       
      return text

#
# this function instantiates m * n Label-widgets and 'grid's them into the previously instantiated LabelFrame
#      
    def createWidgets(self, framenum, rowposn, colposn):
        for rownr in range(0,cellsperframeinY):                       # WITHIN each frame, process .....
          for colnr in range(0,cellsperframeinX):                     #  .... each cell.
            ident = self.get_cell_ident(framenum, cellsperframeinX, \
                                   cellsperframeinY, rownr, colnr)
            self.wlab = Label(self, borderwidth=2, relief=RIDGE, \
                              text=ident, name='w'+ident)
            self.wlab.grid(row=rownr, column=colnr, ipadx=5, ipady=5)
#
# this is the function which has been bound to the Entry-box 'e01' (instantiated above in Application)
#
    def key(self, event):
        if event.char == event.keysym:
          msg = 'Normal Key %r' % event.char
        elif len(event.char) == 1:
          msg = 'Punctuation Key %r (%r)' % (event.keysym, event.char)
        else:
          msg = 'Special Key %r' % event.keysym
        l00.config(text=msg)

If crWidg is not 0, then your self.l00 label is never created. Hence your AttributeError. The instance prefix self. makes that variable global to the class methods.

If crWidg is not 0, then your self.l00 label is never created. Hence your AttributeError. The instance prefix self. makes that variable global to the class methods.

Thanks, but I'm pretty sure that the label (now lab00, BTW) *is* correctly being created, at least it appears in the window. Possibly the problem lies in the fact that the app calls Application three times (with crWidg == 0), thereby generating 3 labels, ALL called lab00. I know that it's possible to assign a name to each instance (using the 'name=' option, e.g. lab00, lab01, lab02), but I haven't yet been able to subsequently address the individual instances using these assigned names. Is this not possible in tkinter (or python?)?

You can use eval() to turn a string into an object.

You can use eval() to turn a string into an object.

Sorry, have tried a couple of examples of eval() but with only syntax-error results:-| . Is eval() a method, a function, an option, or what? Also, what content does the string have to have? Could you give me an example of its use? Thanks.

It's not the eval() function, but the exec() function. Here is a Tkinter example ...

# creating multilabels in Tkinter

from Tkinter import *

root = Tk()

# create a list of labels
labels = []
for k in range(4):
    labels.append(Label(root, text='Label in grid row %d' % k))

# now assign a grid position to each label
for  ix, label in enumerate(labels):
    label.grid(row=ix, column=0, pady=5)

# change a label using labels list index
labels[0].config(fg='blue')
labels[1].config(fg='red')

# give each label in labels list a nice name
exec("lab00 = labels[0]")
exec("lab01 = labels[1]")
exec("lab02 = labels[2]")
exec("lab03 = labels[3]")

# test the nice name
lab02.config(bg='green')
lab03.config(bg='yellow')

root.mainloop()

It's not the eval() function, but the exec() function. Here is a Tkinter example ...

# creating multilabels in Tkinter

from Tkinter import *

root = Tk()

# create a list of labels
labels = []
for k in range(4):
    labels.append(Label(root, text='Label in grid row %d' % k))

# now assign a grid position to each label
for  ix, label in enumerate(labels):
    label.grid(row=ix, column=0, pady=5)

# change a label using labels list index
labels[0].config(fg='blue')
labels[1].config(fg='red')

# give each label in labels list a nice name
exec("lab00 = labels[0]")
exec("lab01 = labels[1]")
exec("lab02 = labels[2]")
exec("lab03 = labels[3]")

# test the nice name
lab02.config(bg='green')
lab03.config(bg='yellow')

root.mainloop()

**Great**, just what I needed. On further investigation, didn't need the 'exec' function (at least, not at the moment, but I'm sure that its time WILL come), but your tip on filling the array with labels and then gridding them was just the ticket. Now my app works just as I wanted. Many, many thanks:p.

PS Just one extra thought; is it possible in tkinter to *simulate* a keypress? Clever as tkinter is, just thought that it might be. Thanks again

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.