-1

I am trying to make a GUI using Tkinter that looks similar to MS-DOS. Aside from the text on the buttons being center justified despite my best efforts to left justify them, it is looking great. I wanted to be able to navigate my menu with the up and down arrow keys using 'enter' to select a menu option. Enter works fine, but when I use either of the arrow keys, I get this error:

Unhandled exception in listener callback
Traceback (most recent call last):
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\site-packages\pynput\_util\__init__.py", line 162, in inner
    return f(self, *args, **kwargs)
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\site-packages\pynput\keyboard\_win32.py", line 280, in _process
    self.on_press(key)
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\site-packages\pynput\_util\__init__.py", line 78, in inner
    if f(*args) is False:
  File "paltoydMockGUI.py", line 27, in on_press
    globals()[names[currentBtn + 1]]['state'] = 'active'
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1492, in __setitem__
    self.configure({key: value})
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1485, in configure
    return self._configure('configure', cnf, kw)
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1476, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
RuntimeError: main thread is not in main loop
Traceback (most recent call last):
  File "paltoydMockGUI.py", line 59, in <module>
    listener.join()
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\site-packages\pynput\_util\__init__.py", line 210, in join
    six.reraise(exc_type, exc_value, exc_traceback)
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\site-packages\six.py", line 702, in reraise
    raise value.with_traceback(tb)
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\site-packages\pynput\_util\__init__.py", line 162, in inner
    return f(self, *args, **kwargs)
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\site-packages\pynput\keyboard\_win32.py", line 280, in _process
    self.on_press(key)
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\site-packages\pynput\_util\__init__.py", line 78, in inner
    if f(*args) is False:
  File "paltoydMockGUI.py", line 27, in on_press
    globals()[names[currentBtn + 1]]['state'] = 'active'
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1492, in __setitem__
    self.configure({key: value})
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1485, in configure
    return self._configure('configure', cnf, kw)
  File "C:\Users\******\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1476, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
RuntimeError: main thread is not in main loop

It seems as if the error has something to do with multi-threading, but I thought I wasn't messing with any of that. Here's my code:

import tkinter as tk
from pynput import keyboard

names = ['documents', 'research', 'I forgot the other buttons', 'button 4', 'button 5']
currentBtn = 0

def mkBtn(names):
    for i in range (len(names)):
        globals()[names[i]] = tk.Button (frame, justify = 'left', font = 'Unifont', bd = 0, bg = 'black', activebackground = 'white', text = str(names[i]), relief = 'flat', activeforeground = 'black', foreground = 'white', width = 200)
        globals()[names[i]].grid(row = i, column = 0, sticky = 'W')

def on_press(key):
    if key == keyboard.Key.esc:
        return False  # stop listener
    try:
        k = key.char  # single-char keys
    except:
        k = key.name  # other keys
    if k in ['up', 'down', 'enter']:  # keys of interest
        global currentBtn
        if k == 'up' and currentBtn != 0:
            globals()[names[currentBtn - 1]]['state'] = 'active'
            globals()[names[currentBtn]]['state'] = 'normal'
            currentBtn -= 1

        elif k == 'down' and currentBtn != len(names) - 1:
            globals()[names[currentBtn + 1]]['state'] = 'active'
            globals()[names[currentBtn]]['state'] = 'normal'
            currentBtn += 1

        else:
            print('works')
            print(k)

root = tk.Tk()

root.attributes("-fullscreen", True)
root.title("Test")
root.geometry('1920x1080')
root.configure(bg='black')

canvas = tk.Canvas(root, width = 1920, height = 1080, bd = '-2' , bg = 'black')
canvas.pack()

canvas.create_rectangle(71, 40, 1849, 1040, outline = 'white', fill = 'black', width = 4)

#main menu frame
frame = tk.Frame(root, bg='black');
frame.place(relx = .045, rely = .05, relwidth = .909, relheight = .9)

#labeling main menu
mkBtn(names)
documents['state'] = 'active'
listener = keyboard.Listener(on_press=on_press)

while 1:
    root.update()
    listener.start()
    listener.join()

All it does so far is create a fullscreen application with a white-bordered rectangle containing 5 buttons labeled according to the names list, as well as the pynput keyboard listener. All I need now is the navigation with arrow keys but I get that pesky error.

Any help would be greatly appreciated!

  • Why do you use a `block` thread and `while` loop in your code? – jizhihaoSAMA Mar 22 '20 at 06:11
  • Read [Tkinter understanding mainloop](https://stackoverflow.com/questions/29158220/tkinter-understanding-mainloop) and All Tcl commands need to originate from the same thread. [Threads/Process and Tkinter](https://stackoverflow.com/questions/26703502) – stovfl Mar 22 '20 at 10:22

1 Answers1

0

This worked in my computer.

import tkinter as tk
from pynput import keyboard

names = ['documents', 'research', 'I forgot the other buttons', 'button 4', 'button 5']
currentBtn = 0

def mkBtn(names):
    for i in range (len(names)):
        globals()[names[i]] = tk.Button (frame, justify = 'left', font = 'Unifont', bd = 0, bg = 'black', activebackground = 'white', text = str(names[i]), relief = 'flat', activeforeground = 'black', foreground = 'white', width = 200)
        globals()[names[i]].grid(row = i, column = 0, sticky = 'W')

def on_press(key):
    if key == keyboard.Key.esc:
        return False  # stop listener
    try:
        k = key.char  # single-char keys
    except:
        k = key.name  # other keys
    if k in ['up', 'down', 'enter']:  # keys of interest
        global currentBtn
        if k == 'up' and currentBtn != 0:
            globals()[names[currentBtn - 1]]['state'] = 'active'
            globals()[names[currentBtn]]['state'] = 'normal'
            currentBtn -= 1

        elif k == 'down' and currentBtn != len(names) - 1:
            globals()[names[currentBtn + 1]]['state'] = 'active'
            globals()[names[currentBtn]]['state'] = 'normal'
            currentBtn += 1

        else:
            print('works')
            print(k)

root = tk.Tk()

root.attributes("-fullscreen", True)
root.title("Test")
root.geometry('1920x1080')
root.configure(bg='black')

canvas = tk.Canvas(root, width = 1920, height = 1080, bd = '-2' , bg = 'black')
canvas.pack()

canvas.create_rectangle(71, 40, 1849, 1040, outline = 'white', fill = 'black', width = 4)

#main menu frame
frame = tk.Frame(root, bg='black');
frame.place(relx = .045, rely = .05, relwidth = .909, relheight = .9)

#labeling main menu
mkBtn(names)
documents['state'] = 'active'
listener = keyboard.Listener(on_press=on_press)

listener.start()

root.mainloop()
jizhihaoSAMA
  • 12,336
  • 9
  • 27
  • 49