返回

Tkinter 实现 Windows 7 Frutiger Aero 风格 Textbox

Ai

打造 Windows 7 风格 Tkinter Textbox:Frutiger Aero 界面实现

老铁们,最近我琢磨着做一个数字助手,想给它整一个 Windows 7 那个年代的 Frutiger Aero 风格。界面设计上,用了 ttkthemes 库的 Aquativo 主题,按钮、滚动条啥的挺好看。但是,碰到了个问题:ttk 不支持 Textbox 样式,结果界面看起来就有点不协调了。就是上面那张图,大家可以看看,那个文本框和周围的风格明显不搭。

还有个小问题,就是在 Linux 上能不能让 Label 控件的背景透明?我想用这个来显示角色。

琢磨了半天,也找了些资料,像 CustomTkinter 什么的,但没找到直接能用的。 所以,咋办呢?咱们一起看看怎么解决这个问题。

一、 问题分析:为啥 Tkinter Textbox 这么难搞?

其实,问题核心就两点:

  1. ttk 的限制: ttk (Themed Tkinter) 主要目的是提供一套与操作系统原生风格更接近的控件。但它的 Textbox 并未提供丰富的样式定制选项。
  2. 设计风格的复杂性: Frutiger Aero 这种设计风格,它有很多特征,像圆角、渐变、阴影、高光,用标准的 Tkinter 控件很难直接实现。

二、 解决之道:多条腿走路

既然直接用 ttk 不行,我们就得想点别的办法。我这儿有几个思路,大家可以根据自己的需求选择:

1. 纯 Tkinter “硬刚”: 模拟样式

这个方法有点“笨”,但也能用。基本思路就是:完全不用 Textbox 控件,而是用 Canvas 控件来模拟文本编辑和显示区域,然后自己绘制边框、背景、滚动条等。

原理:

  • Canvas 是 Tkinter 里一个非常强大的控件,它就像一块画布,你可以在上面画任何东西。
  • 通过绑定鼠标和键盘事件,我们可以在 Canvas 上模拟文本输入、选择、光标移动等操作。
  • 滚动条也可以用 Canvas 自己画,然后和文本显示的区域联动。

代码示例(基本框架):

import tkinter as tk

class CustomTextbox:
    def __init__(self, master, **kwargs):
        self.master = master
        self.canvas = tk.Canvas(master, **kwargs)
        self.canvas.pack(fill=tk.BOTH, expand=True)

        self.text = ""
        self.cursor_pos = 0

        # 绑定事件
        self.canvas.bind("<Button-1>", self.on_click)
        self.canvas.bind("<Key>", self.on_key)
          # 设置初始背景色和边框
        self.canvas.create_rectangle(0, 0, self.canvas.winfo_width(), self.canvas.winfo_height(), fill="white", outline="gray", width=2)

        #绘制文本框的内容
        self.text_id = self.canvas.create_text(5, 5, text=self.text, anchor=tk.NW, font=("Arial", 12), fill="black")


    def on_click(self, event):
        # 处理鼠标点击,设置光标位置等
        # ... (具体实现比较复杂,这里省略)
       self.canvas.focus_set() #获得焦点

    def on_key(self, event):
       # 处理键盘输入
        if event.keysym == "BackSpace":
            if self.cursor_pos > 0:
                self.text = self.text[:self.cursor_pos-1] + self.text[self.cursor_pos:]
                self.cursor_pos -= 1
        elif event.char:  # 只处理可打印字符
            self.text = self.text[:self.cursor_pos] + event.char + self.text[self.cursor_pos:]
            self.cursor_pos += 1
         # 更新 Canvas 上的文本显示
        self.canvas.itemconfig(self.text_id, text=self.text)


root = tk.Tk()
custom_textbox = CustomTextbox(root, width=300, height=200)
root.mainloop()

进阶:

  • 渐变背景: Canvas 支持画渐变。可以使用多个矩形,颜色逐渐变化,来模拟渐变效果。
  • 圆角: 可以用多个小矩形或弧形来拼接圆角。
  • 阴影/高光: 类似渐变的方法,在边框附近绘制不同透明度的颜色块。

安全建议:

这个方案不涉及太多安全问题,因为主要是在本地绘制 UI。

不足:

这个方法虽然能实现效果,但代码会很复杂,需要自己处理很多细节。而且,性能可能也不如原生的 Textbox。

2. 使用富文本控件:tk.Text + Tag

Tkinter 其实自带了一个 tk.Text 控件,它比 ttk.Entry 功能强大得多,支持文本的多种样式。我们可以利用它的 Tag 功能来实现部分 Frutiger Aero 风格。

原理:

  • tk.Text 允许你给文本的不同部分设置不同的 Tag。
  • 每个 Tag 可以定义不同的字体、颜色、背景色等属性。
  • 我们可以通过 Tag 来模拟一部分 Frutiger Aero 风格,比如给文本框的背景加上一层淡蓝色。

代码示例:

import tkinter as tk

root = tk.Tk()

text_widget = tk.Text(root, bg="white", bd=1, relief="sunken", font=("Arial", 12))
text_widget.pack(fill=tk.BOTH, expand=True)

# 定义一个 Tag,用于设置背景颜色
text_widget.tag_configure("aero_bg", background="#e0f0ff")

# 将整个文本框的背景设置为该 Tag
text_widget.tag_add("aero_bg", "1.0", tk.END)
#增加文本框的圆角
text_widget.config(insertbackground="black")  # 插入光标颜色
text_widget.config(highlightthickness=2)  # 焦点高亮边框宽度
text_widget.config(highlightcolor="#70b2ff")
text_widget.config(highlightbackground="#d9d9d9")

root.mainloop()

进阶:

  • 可以创建多个 Tag,分别控制文本的不同部分(比如选中区域、光标所在行等)的样式。

不足:

用 Tag 能实现的样式仍然有限,没法完全还原 Frutiger Aero 的效果。渐变、阴影、圆角等还是很难搞。

3. 外部库“救场”:尝试其他 UI 库

如果不想自己 “硬刚”,也不满足于 tk.Text 的效果,还可以考虑一些其他的 UI 库。这些库可能提供更丰富的控件或样式定制功能。

思路:

  • 看看除了 CustomTkinter 之外,还有没有其他库提供了 Frutiger Aero 风格的组件。
  • 有些库可能支持更方便的自定义控件,你可以用它们来创建自己的 Textbox。

可以尝试的其他 Python GUI 库:

  • PyQt/PySide: 这俩是 Qt 的 Python 绑定。Qt 是一个非常强大的跨平台 GUI 框架,它的样式引擎非常灵活,定制性极强。理论上说,用 Qt 是最有可能实现 Frutiger Aero 效果的。 难度较高。

  • Kivy: 一个比较现代的 UI 框架,专注于触摸和多点触控应用。它的样式系统也比较灵活。但是学习曲线很陡峭。

4. 图片大法:终极备选方案

如果以上所有方法你都不满意,或者觉得太麻烦,那还有个“终极”方案:直接用图片!

原理:

  • 找设计师做一个符合 Frutiger Aero 风格的 Textbox 背景图。
  • 在 Tkinter 里用 Label 或 Canvas 控件显示这个图片。
  • 把 Entry 控件(无边框样式)放在图片上,作为文本输入区域。

代码示例:

import tkinter as tk
from PIL import Image, ImageTk

root = tk.Tk()

# 加载图片(假设图片名为 textbox_bg.png)
try:
    bg_image = Image.open("textbox_bg.png")
    bg_photo = ImageTk.PhotoImage(bg_image)

    # 用 Label 显示图片
    bg_label = tk.Label(root, image=bg_photo)
    bg_label.image = bg_photo  # 保持引用,防止图片被回收
    bg_label.pack()

    # 创建一个 Entry 控件,去掉边框
    entry = tk.Entry(root, bd=0, font=("Arial", 12))
    entry.pack()
    entry.place(x=20,y=17) # 调整位置和大小,让它和背景图对齐
    entry.config(bg="#f2f2f2")
except FileNotFoundError:
    print("找不到背景图片!")

root.mainloop()

不足:

这种方法虽然简单,但也有局限性:

  • 输入框的大小和位置固定了,不好调整。
  • 不能实现文本框的一些高级功能,比如滚动、选择等。
  • 背景不能进行其他的修饰,只能更换。

三、 Linux 上 Label 透明背景

关于 Linux 上 Label 透明背景的问题,这个确实比较麻烦。Tkinter 在不同平台上的表现可能不一致。
解决思路有两个方向:

  1. 尝试 wm_attributes:

    Tkinter 窗口有一个 wm_attributes 方法,可以设置一些窗口属性。其中有一个 -transparentcolor 选项,但它不一定在所有 Linux 系统上都有效。而且设置透明颜色也有点 Hacky。

    import tkinter as tk
    
    root = tk.Tk()
    root.wm_attributes("-transparentcolor", "white") #将白色的地方进行透明化
    
    label = tk.Label(root, text="Hello, 透明!", bg="white") #标签的颜色需和透明颜色一致
    label.pack()
    
    
    root.mainloop()
    

不建议使用这种办法.

  1. 用 Canvas + 图片:

这仍然是最通用的方案。我们可以把角色图像(去除背景)放在 Canvas 上,然后把 Label 放在 Canvas 的对应位置,Label 不设置背景色。这样看起来就像 Label 透明了一样。

import tkinter as tk
from PIL import Image, ImageTk

root = tk.Tk()

# 创建一个 Canvas
canvas = tk.Canvas(root, width=200, height=200, highlightthickness=0)
canvas.pack()

# 加载角色图像(假设是 PNG 格式,带透明通道)
try:
    char_image = Image.open("character.png")
    char_photo = ImageTk.PhotoImage(char_image)
    canvas.create_image(100, 100, image=char_photo)  # 在 Canvas 上显示图像
    canvas.image = char_photo
except FileNotFoundError:
    print("找不到角色图片!")


# 在 Canvas 上创建一个 Label,不设置背景色
label = tk.Label(canvas, text="我是角色!", font=("Arial", 16), fg="black")
label.place(x=50,y=50)

root.mainloop()

如果能直接控制底层,还可以试试 xcompmgr 这样的工具来强制实现窗口透明。不过这种方法依赖特定的桌面环境和配置,也更复杂。

总而言之,要完美实现 Frutiger Aero 风格的 Tkinter Textbox,不是一件容易的事。 可能需要组合多种方法,甚至使用更强大的 GUI 库。不过,只要肯花功夫,总能找到一个比较满意的解决方案。