2

Summarize the Problem

I am looking to use tkinter to implement the actions of tabbing and using the spacebar to cycle and activate UI elements, respectively. I am hoping to do so in a way that mimics the native behavior of OSX, as seen in the attachment below. This works fine on most other widgets in ttk.

I do not have enough reputation to embed this image

This is made up of the following:

  1. Allow components to be "focused" by the user using the tab key
  2. Cause components to become visually highlighted when focused
  3. Component can be triggered using the spacebar while focused
  4. And the tricky part, it uses (close to) the native GUI appearance of the Operating System

Other StackOverflow Questions About This

The closest answer on this site I could find was this question. It may be important to note that the question is asked about Python 2.7.9. This question is interesting, because it suggests that changing a tk.Button() to ttk.Button() alleviates the issue, while when testing the code, I have found the opposite to be true. A tk.Button() will (inelegantly) highlight on OSX, whereas a ttk.Button() provides no visual feedback. Below is the code (with imports modified to run in 2021 on Python 3.X)

import tkinter as tk
from tkinter import ttk

class App(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.master = master

        entry1 = tk.Entry(self)
        entry1.pack()

        entry2 = tk.Entry(self)
        entry2.pack()

        # CHANGE BELOW LINE TO A ttk.Button() TO TEST
        button1 = tk.Button(self, text="Test", command=self.yup)
        button1.pack()

    def yup(self):
        print("yup")

root = tk.Tk()
app = App(root).pack()

root.mainloop()

My Attempted Solutions

Solution One: Implement buttons normally using ttk.Button() with ttk.Style() to indicate focus

Pros:

  • Uses native OS style
  • Accepts focus via tab and activation via spacebar

Cons:

  • Does not visually indicate focus unless forced to with ttk.Style()
  • To the extent of my knowledge cannot be given a highlighted border by ttk.Style(), so we must settle for colored text

Example Code:

from tkinter import Tk
from tkinter import ttk

root = Tk()

def one():
    print("one")
def two():
    print("two")

style = ttk.Style()
style.configure('C.TButton')

style.map('C.TButton',
    foreground = [('pressed','red'),('focus','blue')],
    background = [('pressed','!disabled','red'),('focus','white')],
    relief=[('pressed', 'sunken'),
            ('!pressed', 'raised')])
# Define lower GUI
B1 = ttk.Button(root, text='Button One', command=one, style='C.TButton')
B1.pack(padx=20, pady=(20,0))
B2 = ttk.Button(root, text='Button Two', command=two, style='C.TButton')
B2.pack(padx=20, pady=10)

root.mainloop()

Solution Two: Use tk.Button() instead

Pros:

  • Accepts focus via tab and activation via spacebar
  • Natively highlights button using a border

Cons:

  • Does not look that appealing, border is misaligned and a plain rectangle
  • I cannot get many parameters to work properly on OSX, particularly activebackground and highlightthickness, limiting aesthetic options.

Example Code:

import tkinter as tk
from tkinter import ttk

root = tk.Tk()

def one():
    print("one")
def two():
    print("two")
B1 = tk.Button(root, text='Button One', command=one)
B1.pack(padx=20, pady=(20,0))
B2 = tk.Button(root, text='Button Two', command=two)
B2.pack(padx=20, pady=10)

root.mainloop()

Solution Three: Use the tkmacosx library's Button()

Pros:

  • Custom made for this problem
  • Highlights on tab-press with OSX-like style
  • Really, just everything I'm looking for

Cons:

  • Does not trigger button on spacebar

This last part is interesting, because based on the documentation (takefocus), this should be the expected behavior. On my machine, this is not the case (Catalina 10.15.7)

Example Code:

from tkinter import Tk
from tkmacosx import Button

root = Tk()

def one():
    print("one")
def two():
    print("two")
B1 = Button(root, text='Button One', command=one)
B1.pack(padx=20, pady=(20,0))
B2 = Button(root, text='Button Two', command=two)
B2.pack(padx=20, pady=10)

root.mainloop()

Concluding

Historically I understand that tkinter and OSX have not always played together perfectly, and that if I want more precise native control, I might switch to QT. I am immensely thankful for the existence of tkinter, and hope I am not asking too much.

I do want to be sure however that I'm not making an error before I try forking a repo or pursuing other solutions.

Regarding tkmacosx, it seems like this solution should be working the way it is described in the documentation, and I was hoping to get confirmation of this problem from another user, to see if it would be appropriate to file an issue on the github page.

Thank you very much for reading this post. Please feel free to ask for any additional info!

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
jpodolski
  • 21
  • 5
  • 1
    You are right about the issue, the `spacebar` doesn't trigger the button of tkmacosx. – Saad Oct 02 '21 at 07:39
  • Thank you Saad! The dev replied on GitHub and told me this will be fixed in the next update. I do still believe that this should be included functionality in default tkinter however. – jpodolski Oct 04 '21 at 22:58

0 Answers0