Tkinter Canvas - 出现 2px 内部填充

问题描述 投票:0回答:1

我注意到 Tkinter Canvas 实现有一个奇怪的行为。这是一个小片段来强调这种行为。

shall_configure_innerframe = False
shall_configure_periodically = False


from tkinter import Tk, Canvas
from tkinter.ttk import Style, Frame, Label

win = Tk()

# Styles
Style().configure('outer.TFrame', background='black')
Style().configure('inner.TFrame', background='red')

# Outer frame (to highlight Canvas area)
frame = Frame(win, style='outer.TFrame')
frame.pack(expand=True, fill='both', padx=1, pady=1)

# Canvas
canvas = Canvas(frame)
canvas.pack(expand=True, fill='both', padx=3, pady=3)

# Inner frame
innerframe = Frame(canvas, style='inner.TFrame')
win_id = canvas.create_window(0, 0, anchor='nw', window=innerframe)

# Let's add some content to actually see the frame
Label(innerframe, text='AAA').pack()

# Callback when canvas is configured (to set window width to the full canvas)
def on_canvas_configure(event):
    canvas_width = event.width
    canvas.itemconfig(win_id, width=canvas_width)
canvas.bind('<Configure>', on_canvas_configure)

# Callback when innerframe gets configured
def on_innerframe_configure(event):
    region = canvas.bbox('all')
    print(f'onscroll: {region}')
    canvas.config(scrollregion=region)

if shall_configure_innerframe:
    innerframe.bind('<Configure>', on_innerframe_configure)

# Function to periodically call the on_innerframe_configure
def configure_periodically(*args, **kwargs):
    on_innerframe_configure(None)
    win.after(2000, configure_periodically)
    
if shall_configure_periodically:
    win.after(2000, configure_periodically)

win.mainloop()

如您所见,窗口的“核心”部分是内部有框架的画布,然后调整该框架的大小以适合画布(宽度)。

根据一开始两个布尔值的配置,我可以得到这里突出显示的三个结果之一:

基本上:

  • 如果我从未配置
    scrollregion
    参数(因此
    shall_configure_innerframe = False
    shall_configure_periodically = False
    ),我会得到结果 A:内部框架与 Canvas 边框对齐
  • 如果我稍后配置
    scrollregion
    参数(因此
    shall_configure_innerframe = False
    shall_configure_periodically = True
    ),我会得到结果 A,直到计时器耗尽,然后一旦调用该函数,我就会得到结果 B:内部框架首先对齐与画布边框一起,然后出现 2px 垂直填充(但不在水平侧)
  • 如果我尽快配置
    scrollregion
    参数(因此
    shall_configure_innerframe = True
    shall_configure_periodically
    并不重要),我会得到结果 C:内框在垂直和水平两侧都有 2px 填充

我在后一种情况下注意到的是,内部框架发生了移动,而不是调整了大小,因为最后两个像素消失在画布之外(我在实际应用程序中注意到了这一点,其中内容消失了)。

在所有情况下,计算出的 bbox 始终相同:

onscroll: (0, 0, 382, 19)

为什么画布会这样?目前我只是在右侧添加 4px 填充,但这对我来说似乎不正确。

注意:当然真正的应用程序有点复杂,我实际上需要设置滚动区域

python tkinter tkinter-canvas
1个回答
0
投票

摆弄我的代码(以及问题评论中链接的 Сергей Кох),我发现实际上 Canvas 的默认实现周围有一个 2 像素的突出显示边框。

注意:关于问题的图像,我删除了屏幕缩放,因此我也可以“测量”用户界面上的像素

例如,当我简单地将背景设置为黄色(使用

canvas = Canvas(frame, bg='yellow')
)而不创建窗口时会发生什么:

如你所见,从外面开始有

  • 外框造成的3px黑边
  • 2 个灰色像素
  • 黄色背景区域,164像素宽

on_canvas_configure
函数中,
event.width
参数设置为168,即黄色+灰色。

我无法找到计算画布实际视图宽度的函数。但是我发现画布将原点设置在黄色区域的开头,因此通过

canvas.canvasx(0)
我能够理解突出显示宽度是多少((0, 0) 屏幕坐标对应于画布的左上角)画布小部件,(0, 0) 画布坐标到黄色区域的开头)。

因此,我将 on_canvas_configure 函数更改为以下函数:

def on_canvas_configure(event):
    canvas_width = event.width + 2 * canvas.canvasx(0)
    canvas.itemconfig(win_id, width=canvas_width)

如您所见,内部小部件现在是画布的正确宽度,并且不会超出该宽度。

为了删除周围的 2px 边框,我只需要显式删除突出显示即可:

canvas = Canvas(frame, highlightthickness=0)

(在这两种情况下我都设置了

shall_configure_innerframe = True
,因为这是我的真实用例)。

TL;DR:您应该始终记住,如果没有其他说明,画布将具有 2px 的灰色边框。该边框将被考虑在项目宽度/高度中,因此视图区域将减少边框大小的两倍。要解决此问题,请将

highlightthickness
设置为 0 或在设置内容大小时考虑其大小

© www.soinside.com 2019 - 2024. All rights reserved.