Home > front end >  ttk, How to position heading inside Labelframe instead of on the border?
ttk, How to position heading inside Labelframe instead of on the border?

Time:03-25

I am trying to create an own ttk Theme based on my company's CI. I took the Sun Valley theme as starting point and swapped out graphics, fonts and colors.

However I am stuck on the Label frame. I am trying to position the Label within the frame, kind of like a heading. I.e. there should be some margin between top edge and label, and appropriate top-padding for the content (child widgets).

Now:

 -- Label ------
| ...

Desired:

 ---------------
| Label
| ...

I tried to set the padding option:

  • within the Layout
  • on TLabelframe itself
  • on TLabelframe.Label

but the label did not move a pixel. How to achieve this?

Generally I am very confused about what identifiers and options are legal within ttk:style layout, ttk:style element and ttk:style configure, because documentation is hazy and scattered all over the 'net, and there are no error messages whatsoever. Any helpful tips?

Edit: What I found out since posting:

  • The Labelframe label is a separate widget altogether, with the class TLabelframe.Label.
  • It is possible to override its layout and add a spacer on top, shifting the text down.
  • However, the label widget is v-centered on the frame line. If its height increases, it pushes "upward" as much as downward. I found no way to alter the alignment w.r.t. to the actual frame.
  • It might be possible to replace Labelframe altogether with a custom Frame subclass with the desired layout. But that means changing the "client" code in many places. :-/

CodePudding user response:

It is relatively easy to place ttk.Labelframe text below, on or above the relief graphic. This example uses the text attribute but labelwidget can also be used. In order for the relief to be visible the background color of Labelframe.Label must be set to "".

import tkinter as tk
from tkinter import font
from tkinter import ttk

message = "Hello World"

master = tk.Tk()
style = ttk.Style(master)
style.theme_use(themename = "default")

actualFont = font.Font(
    family = "Courier New", size = 20, weight = "bold")
style.configure(
    "TLabelframe.Label", background = "", font = actualFont)

frame = ttk.LabelFrame(
    master, labelanchor = "n", text = message)
frame.grid(sticky = tk.NSEW)
frame.rowconfigure(0, weight = 1)
frame.columnconfigure(0, weight = 1)

def change_heading():
    if frame["text"][0] == "\n":
        frame["text"] = f"{message}\n"
    else:
        frame["text"] = f"\n{message}"

button = tk.Button(
    frame, text = "Change", command = change_heading)
button.grid(sticky = "nsew")

master.mainloop()

CodePudding user response:

Looking through the source of the Labelframe widget, I found that:

  • The label is either placed vertically-centered on the frame's border, or flush above it, depending on the -labeloutside config option. (for default NW anchor)
  • i.e. by adding whitespace on top of the text by any means, the label box will extend upwards the same amount as downwards, creating a "dead space" above the frame.
  • There might still be a way to get it "inside" by increasing the border width, but I couldn't get it to work.

I now used the labeloutside option to make a "tab-like" heading.

    # ... (define $images array much earlier) ...
    ttk::style element create Labelframe.border image $images(card2) \
        -border 6 -padding 6 -sticky nsew
    ttk::style configure TLabelframe -padding {8 8 8 8} -labeloutside 1 -labelmargins {2 2 2 0}
    ttk::style element create Label.fill image $images(header2) -height 31 -padding {8 0 16 0} -border 1

With suitable images, this is nearly what I was aiming for, only that the header does not stretch across the full frame width. Tkinter elements use a "9-patch"-like subdivision strategy for images, so you can make stretchable frames using the -border argument for element create.

Result is approximately this:

 ------------- 
| Heading     |
 ------------- ---------------- 
| ...                          |
  • Related