Tkinter Notebook小部件中标签内的复杂格式是否可能?

时间:2017-07-04 08:06:33

标签: python tkinter tabs

我刚刚为我的日志控制台添加了一些不错的格式,根据消息类型,消息前缀的颜色不同:

enter image description here

为了做到这一点,我使用了标签(tag_config())和Font()来使事情变得粗体:

from tkinter import font, Tk, END
from tkinter.scrolledtext import ScrolledText


def log_info(msg, widget):
  log(msg, "info", widget)

def log_error(msg, widget):
  log(msg, "error", widget)

def log_warn(msg, widget):
  log(msg, "warn", widget)

def log(msg, type, widget):
  msg_prefix = font.Font(widget, widget.cget("font"))
  msg_prefix.configure(weight="bold")
  if type == "info":
    widget.insert(END, "INFO: ", "info")
    widget.insert(END, msg + "\n")
  elif type == "error":
    widget.insert(END, "ERROR: ", "error")
    widget.insert(END, msg + "\n")
  elif type == "warn":
    widget.insert(END, "WARNING: ", "warn")
    widget.insert(END, msg + "\n")
  else:
    print("Unknown type of log message. Available types are 'info', 'error' and 'warn'")

  widget.tag_config("info", font=msg_prefix)
  widget.tag_config("error", foreground="red", font=msg_prefix)
  widget.tag_config("warn", foreground="orange", font=msg_prefix)

window = Tk()
window.geometry('970x45')
box = ScrolledText(window, width=70, height=7)
box.pack()
log_info("This is an information log message", box)
log_error("This is an error log message", box)
log_warn("This is a warning log message", box)

window.mainloop()

但是我很难在这个控制台所在的选项卡中添加类似的功能。每次在控制台中记录错误日志消息时,它所在的选项卡都会更新,以显示当前记录的错误数:

enter image description here

但是我想让(4)(来自上面的屏幕截图)用红色着色。我无法找到标签标签的字体格式:

enter image description here

原因:我发现日志记录控制台经常被忽略,因为我的小部件中的其他选项卡是交互发生的地方。由于我希望保持分离以防止在屏幕上聚集太多东西,同时我试图以非侵入方式通知用户发生了错误(其他类型的日志消息是不那么重要。)

3 个答案:

答案 0 :(得分:2)

如果您正在使用ttk.Notebook,答案是您只能使用样式为所有选项卡配置标签文本样式。所以你可以让所有标签前景红...

nb = ttk.Notebook(master, style='TNotebook')
...
style = ttk.Style()
# This will change the foreground color of ALL tabs to red.
style.configure('TNotebook.Tab', foreground='red')

您还可以使用选项卡compound选项,该选项可让您在选项卡上放置文本和图像。您可以单独更改给定选项卡上的文本或图像。但要做你要求的,你的'(4)'必须是一个图像,这意味着您必须等待加载许多预先存在的图像,具体取决于您预期的错误数量。

答案 1 :(得分:1)

简答:

不,从头开始是不可能的,因为ttk小部件的外观很大程度上取决于使用中的主题和/或平台 - 如果没有跳转到tk环境/难以保持所需的布局,很难实现。 / p>

但你的问题真的很感兴趣,所以我试着自己实现这个功能。

长答案:

首先,您需要知道,每个小部件都有layout structure,由元素层次结构表示。

在我的Win计算机上,默认主题为vistaTab的布局为:

>>> s = ttk.Style()
>>> print(s.layout('TNotebook.Tab'))

#   formatted result, actual result is one-line string
[('Notebook.tab', {
    'children': [(
        'Notebook.padding', {
            'children': [(
                'Notebook.focus', {
                    'children': [(
                        'Notebook.label', {
                            'sticky': '',
                            'side': 'top'
                        })],
                    'sticky': 'nswe',
                    'side': 'top'
                })],
            'sticky': 'nswe',
            'side': 'top'
        })],
    'sticky': 'nswe'
})]

如您所见 - 有一个Notebook.label元素。这些元素处理所有选项:

>>> print(s.element_options('Notebook.label'))
('-compound', '-space', '-text', '-font', '-foreground', '-underline', '-width', '-anchor', '-justify', '-wraplength', '-embossed', '-image', '-stipple', '-background')

就是这样 - 你不能在标签内部分地格式化文本,因为-font-foreground选项适用于整个字符串。

但是,如果我们使用其他标签/文字更改此布局结构会怎么样? 这看起来是个好主意,我们可以使用第一个文本作为标题标题,第二个文本用于计数器!

s = ttk.Style()
s.configure('SOExample.TNotebook')
s.layout('SOExample.TNotebook.Tab', [('Notebook.tab', {
    'sticky': 'nswe',
    'children': [(
        'Notebook.padding', {
            'sticky': 'nswe',
            'children': [(
                'Notebook.focus', {
                    'sticky': 'nswe',
                    'children': [(
                        'Notebook.label', {
                            'sticky': '',
                            'side': 'left'
                        }), (
                        'Notebook.text', {
                            'sticky': '',
                            'side': 'right'
                        })],
                    'side': 'top'
                })],
            'side': 'top'
        })]})])

不幸的是,显然,它不会起作用,因为两个元素对相同的选项做出反应(textfont等等。

根据文档here

  

元素选项的值取自:

     
      
  • 包含该小部件的小部件中具有相同名称和类型的选项   元件;

  •   
  • 由样式图和当前状态指定的动态设置;

  •   
  • style configure指定的默认设置;或

  •   
  • 元素内置的选项默认值。

  •   

但是,我认为可以将第二个text选项“映射”为text2用于Tab,但是如果有自己的Python,则看起来不可能完成任务!

结论:

很长的答案是尝试实现这样的功能,这导致另一个问题“如何向自定义类窗口小部件添加选项并将它们绑定到元素选项”。更严格的解决方案是使用标签的compound行为,因为您可以将任何int转换为图像/ 64编码的图像字符串,但已经提到了此选项。

答案 2 :(得分:0)

我会将您的日志窗口声明为LogWindow类的实例。这可能不是完美的Python,但你明白了。您也可以移动tag_config命令。 __log成为私有方法,因此不需要严格的数据检查。

class LogWindow:
  def __init__(self):
    window = Tk()
    window.geometry('970x45')
    self.widget = ScrolledText(window, width=70, height=7)
    self.widget.pack()

  def info(msg):
    self.widget.insert(END, "INFO: ", "info")
    __log(msg, "info")

  def error(msg, widget):
    self.widget.insert(END, "ERROR: ", "error")
    __log(msg, "error")

  def warn(msg):
    self.widget.insert(END, "WARNING: ", "warn")
    __log(msg, "warn")

  def __log(msg, type):
    msg_prefix = font.Font(self.widget, widget.cget("font"))
    msg_prefix.configure(weight="bold")
    self.widget.insert(END, msg + "\n")
    self.widget.tag_config("info", font=msg_prefix)
    self.widget.tag_config("error", foreground="red", font=msg_prefix)
    self.widget.tag_config("warn", foreground="orange", font=msg_prefix)

log = LogWindow();

log.info("This is an information log message")
log.error("This is an error log message")