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
相关产品推荐
相关产品推荐

