You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tkinter无限画布视图内元素跟踪异常问题排查

Tkinter无限画布视图内元素跟踪问题排查

问题背景

实现Tkinter无限画布的视图内元素跟踪功能时遇到坐标判断异常,具体情况如下:

  • 通过__init__方法获取的viewing_box初始输出为(0, 0, 378, 265),对应宽度378、高度265。
  • 矩形元素宽高固定为100,但坐标不符合预期;视图宽高保持不变,移动逻辑正常。
  • 仅使用find_overlapping无法满足需求,因Tkinter该算法要求元素至少51%在视图内才会被跟踪,故自定义了元素可见性判断逻辑。

异常跟踪输出

出现异常时的跟踪日志:

1 false
1 false
here ([0.0, -60.0], [100.0, 40.0]) (0, 60, 378, 325)

关键逻辑代码

异常触发逻辑位于_scan_view方法中,相关代码片段:

if not viewable:
    current = self.itemcget(item,'tags')
    if isinstance(current, tuple):
        new = current-('viewable',)
    else:
        print('here',points, (x1,y1,x2,y2))
        new = ''
        self.inview_items.discard(item)

核心疑问

自定义可见性判断逻辑为if x1 <= point[0] <= x2 and y1 <= point[1] <= y2:,即判断元素任意顶点是否在视图范围内。但实际运行中出现坐标不符合预期的情况,怀疑存在两套坐标系统冲突,但无法定位具体原因。

完整代码

import tkinter as tk

class InfiniteCanvas(tk.Canvas):

    def __init__(self, master, **kwargs):
        super().__init__(master, **kwargs)
        self.inview_items   = set()  #in view
        self.niview_items   = set()  #not in view
        self._xshifted      = 0     #view moved in x direction
        self._yshifted      = 0     #view moved in y direction
        self._multi         = 0
        self.configure(confine=False,highlightthickness=0,bd=0)
        self.bind('<MouseWheel>',       self._vscroll)
        self.bind('<Shift-MouseWheel>', self._hscroll)
        root.bind('<Control-KeyPress>',lambda e:setattr(self,'_multi', 10))
        root.bind('<Control-KeyRelease>',lambda e:setattr(self,'_multi', 0))
        print(self.viewing_box())
        return None

    def viewing_box(self):
        'returns x1,y1,x2,y2 of the currently visible area'
        x1 = 0 - self._xshifted
        y1 = 0 - self._yshifted
        x2 = self.winfo_reqwidth()-self._xshifted
        y2 = self.winfo_reqheight()-self._yshifted
        return x1,y1,x2,y2

    def _scan_view(self):
        x1,y1,x2,y2 = self.viewing_box()
        for item in self.find_withtag('viewable'):
            #check if one felt over the edge
            coords = self.coords(item)
            points = tuple(
                coords[x:x + 2] for x in range(0, len(coords), 2))
            viewable = False
            for point in points:
                if x1 <= point[0] <= x2 and y1 <= point[1] <= y2:
                    #if any point is in viewing box
                    viewable = True
                    print(item, 'true')
                else:
                    print(item, 'false' )
            if not viewable:
                current = self.itemcget(item,'tags')
                if isinstance(current, tuple):
                    new = current-('viewable',)
                else:
                    print('here',points, (x1,y1,x2,y2))
                    new = ''
                    self.inview_items.discard(item)
                self.itemconfigure(item,tags=new)
        for item in self.find_overlapping(x1,y1,x2,y2):
            #check if item inside of viewing_box not in inview_items
            if item not in self.inview_items:
                self.inview_items.add(item)
                current = self.itemcget(item,'tags')
                if isinstance(current, tuple):
                    new = current+('viewable',)
                elif isinstance(current, str):
                    if str:
                        new = (current, 'viewable')
                    else:
                        new = 'viewable'
                    self.itemconfigure(item,tags=new)
        print(self.inview_items)

    def _create(self, *args):
        if (current:=args[-1].get('tags', False)):
            args[-1]['tags'] = current+('viewable',)
        else:
            args[-1]['tags'] = ('viewable',)
        ident = super()._create(*args)
        self._scan_view()
        return ident

    def _hscroll(self,event):
        offset = int(event.delta/120)
        if self._multi:
            offset = int(offset*self._multi)
        canvas.move('all', offset,0)
        self._xshifted += offset
        self._scan_view()

    def _vscroll(self,event):
        offset = int(event.delta/120)
        if self._multi:
            offset = int(offset*self._multi)
        canvas.move('all', 0,offset)
        self._yshifted += offset
        self._scan_view()

root = tk.Tk()
canvas = InfiniteCanvas(root)
canvas.pack(fill=tk.BOTH, expand=True)

size, offset, start = 100, 10, 0
canvas.create_rectangle(start,start, size,size, fill='green')
canvas.create_rectangle(
    start+offset,start+offset, size+offset,size+offset, fill='darkgreen')

root.mainloop()

内容的提问来源于stack exchange,提问作者Thingamabobs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 12:15:49