簡體   English   中英

ttk,如何將 position 標題放在 Labelframe 內而不是邊框上?

[英]ttk, How to position heading inside Labelframe instead of on the border?

我正在嘗試根據我公司的 CI 創建自己的 ttk 主題。 我以Sun Valley 主題為起點並交換了圖形 fonts 和 colors。

但是我被困在 Label 框架上。 我正在嘗試在框架顯示 position Label,有點像標題。 即頂部邊緣和 label 之間應該有一些邊距,並且內容(子部件)有適當的頂部填充。

現在:

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

期望:

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

我試圖設置padding選項:

  • 在布局內
  • 在 TLabelframe 本身
  • 在 TLabelframe.Label 上

但是 label 沒有移動一個像素。 如何做到這一點?

一般來說,我對ttk:style layoutttk:style elementttk:style configure中哪些標識符和選項是合法的感到非常困惑,因為文檔模糊不清,散布在 .net 各處,並且沒有任何錯誤消息。 有什么有用的提示嗎?

編輯:發布后我發現了什么:

  • Labelframe label 與 class TLabelframe.Label完全是一個單獨的小部件。
  • 可以覆蓋它的布局並在頂部添加一個間隔符,將文本向下移動。
  • 但是,label 小部件在框架線上以 v 為中心。 如果它的高度增加,它會“向上”推動和向下推動一樣多。 我找不到改變 alignment w.r.t 的方法。 到實際的框架。
  • 可以用具有所需布局的自定義Frame子類完全替換Labelframe 但這意味着要在很多地方更改“客戶端”代碼。 :-/

這可以通過更改布局定義來完成,以便文本元素由 Labelframe 布局保留並且 Layoutframe.Label 不再繪制文本元素。 添加一點填充可確保包含的小部件使 label 保持清晰。

在 Windows 10 上運行的示例程序的屏幕截圖

示例代碼:

import sys
import tkinter as tk
import tkinter.ttk as ttk


class CustomLabelframe(ttk.Labelframe):
    def __init__(self, master, **kwargs):
        """Initialize the widget with the custom style."""
        kwargs["style"] = "Custom.Labelframe"
        super(CustomLabelframe, self).__init__(master, **kwargs)

    @staticmethod
    def register(master):
        style = ttk.Style(master)
        layout = CustomLabelframe.modify_layout(style.layout("TLabelframe"), "Custom")
        style.layout('Custom.Labelframe.Label', [
            ('Custom.Label.fill', {'sticky': 'nswe'})])
        style.layout('Custom.Labelframe', [
            ('Custom.Labelframe.border', {'sticky': 'nswe', 'children': [
                ('Custom.Labelframe.text', {'side': 'top'}),
                ('Custom.Labelframe.padding', {'side': 'top', 'expand': True})
            ]})
        ])
        if (style.configure('TLabelframe')):
            style.configure("Custom.Labelframe", **style.configure("TLabelframe"))
        # Add space to the top to prevent child widgets overwriting the label.
        style.configure("Custom.Labelframe", padding=(0,12,0,0))
        style.map("Custom.Labelframe", **style.map("TLabelframe"))
        master.bind("<<ThemeChanged>>", lambda ev: CustomLabelframe.register(ev.widget))
        
    @staticmethod
    def modify_layout(layout, prefix):
        """Copy a style layout and rename the elements with a prefix."""
        result = []
        for item in layout:
            element,desc = item
            if "children" in desc:
                desc["children"] = HistoryCombobox.modify_layout(desc["children"], prefix)
            result.append((f"{prefix}.{element}",desc))
        return result


class App(ttk.Frame):
    """Test application for the custom widget."""
    def __init__(self, master, **kwargs):
        super(App, self).__init__(master, **kwargs)
        self.master.wm_geometry("640x480")
        
        frame = self.create_themesframe()
        frame.pack(side=tk.TOP, fill=tk.BOTH)
        
        for count in range(3):
            frame = CustomLabelframe(self, text=f"Frame {count}", width=160, height=80)
            frame.pack(side=tk.TOP, expand=True, fill=tk.BOTH)
            button = ttk.Button(frame, text="Test")
            button.pack(side=tk.LEFT)
        self.pack(side=tk.TOP, expand=True, fill=tk.BOTH)

    def create_themesframe(self):
        frame = ttk.Frame(self)
        label = ttk.Label(frame, text="Theme: ")
        themes = ttk.Combobox(frame, values=style.theme_names(), state="readonly")
        themes.current(themes.cget("values").index(style.theme_use()))
        themes.bind("<<ComboboxSelected>>", lambda ev: style.theme_use(ev.widget.get()))
        label.pack(side=tk.LEFT)
        themes.pack(side=tk.LEFT)
        return frame


def main(args=None):
    global root, app, style
    root = tk.Tk()
    style = ttk.Style(root)
    CustomLabelframe.register(root)
    app = App(root)
    try:
        import idlelib.pyshell
        sys.argv = [sys.argv[0], "-n"]
        root.bind("<Control-i>", lambda ev: idlelib.pyshell.main())
    except Exception as e:
        print(e)
    root.mainloop()
    return 0

if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))

ttk.Labelframe文本放置在浮雕圖形下方、上方或上方相對容易。 此示例使用文本屬性,但也可以使用labelwidget 為了使浮雕可見, Labelframe.Label的背景顏色必須設置為“”。

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()

查看Labelframe 小部件的源代碼,我發現:

  • label 要么垂直居中放置在框架的邊框上,要么在其上方齊平,具體取決於-labeloutside配置選項。 (對於默認NW錨點)
  • 即通過任何方式在文本頂部添加空白,label 框將向上延伸與向下延伸相同的量,在框架上方創建“死空間”。
  • 可能仍然有辦法通過增加邊框寬度使其“進入”內部,但我無法讓它工作。

我現在使用 labeloutside 選項制作一個“類似制表符”的標題。

    # ... (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

有了合適的圖像,這幾乎就是我的目標,只是 header 沒有延伸到整個幀寬度。 Tkinter 元素對圖像使用類似“9-patch”的細分策略,因此您可以使用element create-border參數制作可拉伸的框架。

結果大概是這樣的:

+-------------+
| Heading     |
+-------------+----------------+
| ...                          |

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM