Python 在坐标处找不到 tkinter 项目

Posted

技术标签:

【中文标题】Python 在坐标处找不到 tkinter 项目【英文标题】:Python can't find tkinter item at coordinates 【发布时间】:2020-04-11 14:29:07 【问题描述】:

我有这个代码:

for item in items:
    print ( "Item: ", item, mycanvas.coords (item) )

print ("Item at 26.0, 188.0 ", mycanvas.find_closest(26.0, 188.0))
print ("Item at 998.0, 594.0 ", mycanvas.find_closest(998.0, 594.0))

当我运行它时,我得到:

('Item: ', 1, [985.0, 565.0])
('Item: ', 8, [25.0, 25.0])
('Item: ', 15, [505.0, 25.0])
('Item: ', 28, [1362.0, 31.0])
('Item: ', 35, [1020.0, 119.0])
('Item: ', 42, [1050.0, 583.0])
('Item: ', 49, [25.0, 25.0])
('Item: ', 56, [26.0, 188.0])
('Item: ', 63, [998.0, 594.0])
('Item: ', 70, [1152.0, 38.0])
('Item at 26.0, 188.0 ', (57,))
('Item at 998.0, 594.0 ', (64,))

最后两行应分别读取找到的项目5663

我在屏幕上有 10 张图片,我试图找出点击的是哪一张。更糟糕的是 X,Y 坐标被传递给屏幕位置而不是画布位置,我必须弄清楚如何转换:

def popup(event):
    x = mycanvas.canvasx(event.x_root)
    y = mycanvas.canvasy(event.y_root)
    print( "Adjusted x, y: ", x, y )
    FoundItem=mycanvas.find_closest(x, y)
    print( "Found Item @ x, y: ", FoundItem, event.x_root, event.y_root )
    FoundTuple=mycanvas.find_overlapping(x, y, x+1, y+1)
    print( "Found Tuple: ", FoundTuple )
    # use coordinates relative to the canvas
#    ItemNdx=items.index(FoundItem)
#    print( "ItemNdx: ", ItemNdx )
    menu.tk_popup(event.x_root, event.y_root)

在 Python 的 Tkinter 中,找出用户点击了哪个图像(项目/标签 ID)的最简单方法是什么?


使用窗口图像监控图像

了解画布的外观:

这是三个显示器 eDP-1-1 之一的部分。给出最坏的情况,打开四个窗口:

gedit 有问题的源代码在底部打开。 带有此问答的 Firefox 浏览器在上面打开。 Nautilus 窗户在上面。 Gnome 终端在顶部。

窗口堆叠顺序还不完善,因为wmctrl 用于获取桌面上所有打开的窗口,尚未按堆叠顺序进行任何排序。

所以用户会点击最接近他们想要选择的图像的左上角。用户也可以点击没有被窗口覆盖的空白监视器区域来选择监视器图像。 - 本网站的火狐浏览器

标签电流也是一关

我输入了这段代码:

tag_current=event.widget.find_withtag("current")
print ("tag_current: ", tag_current)

结果是这样的:

('Item: ', 1, [985.0, 565.0])
('Item: ', 8, [25.0, 25.0])
('Item: ', 15, [505.0, 25.0])
('Item: ', 28, [1362.0, 31.0])
('Item: ', 35, [949.0, 167.0])
('Item: ', 42, [1050.0, 583.0])
('Item: ', 49, [83.0, 52.0])
('Item: ', 56, [26.0, 188.0])
('Item: ', 63, [998.0, 594.0])
('Item: ', 70, [25.0, 25.0])
('Item: ', 77, [1091.0, 644.0])
('Item at 26.0, 188.0 ', (70,))
('Item at 998.0, 594.0 ', (64,))
('tag_current: ', (41,))

tag_current 应该是 42,但被报告为 41

63 被报告为 6456 被报告为 70 (之前报告为 57 上次运行作业)的问题非常相似。


12 小时更新

12 小时后,我越来越近了。需要将窗口坐标(三个显示器的整个桌面)转换为画布坐标(其中一个显示器上的一个 Python 程序):

def popup(event):
    global FoundItem, MouseXY
    x = mycanvas.canvasx(event.x)
    y = mycanvas.canvasy(event.y)
    MouseXY=(x, y)
    print( "Adjusted x, y: ", x, y )
    FoundItem=mycanvas.find_closest(x, y)

希望今晚能解决问题...

【问题讨论】:

1) 为所有图像项分配标签,例如img = mycanvas.create_image(..., tag='image'); 2)将鼠标单击事件绑定到这些图像项,例如mycanvas.tag_bind('image', '<Button-1>', on_image_click); 3) 使用mycanvas.find_withtag('current') 获取on_image_click() 函数中点击的图像。 @acw1668 图像是桌面上打开的窗口。图片的数量是无限的,所以用无限的“on_image_click”语句硬编码标签是不可能的。 该图像不是桌面上打开的窗口,它是一个放在打开窗口位置的画布图像项。所以需要调用mycanvas.create_image(...)来创建图片项,只需在mycanvas.create_image(...)语句中添加tag='whatever'即可。 @acw1668 我的画布包含 10 个图像:3 个监视器(通过屏幕截图获得的 HDMI-1、DP-1-1 和 eDP-1-1)和使用wmctrl -lG 获得的 7 个窗口。项目列表中填充有item=mycanvas.create.image(...),后跟items.append( [item] ) 我将在接下来的几天内尝试一个新的程序版本,其标签设置为xrandr 监视器名称和wmctrl 窗口编号。谢谢。 【参考方案1】:

找出“用户点击了哪个图像(标签/标签ID)”的方法是使用标签current。规范的 tcl/tk 文档对 current 标记进行了如下说明:

标签current由Tk自动管理;它适用于 当前项,即绘制区域覆盖鼠标光标位置的最顶层项(不同的项类型在 不同的方式;有关详细信息,请参阅单个项目类型文档)。 如果鼠标不在画布小部件中或不在项目上,则 没有项目具有当前标签。

这是一个人为的示例,它将打印出与您单击的任何项目相关的标签。请注意,即使您滚动画布,它也能正常工作。

import tkinter as tk

def callback(event):
    tags = canvas.itemcget("current", "tags")
    label.configure(text=f"you clicked on tags")

root = tk.Tk()
canvas = tk.Canvas(root, width=300, height=300)
label = tk.Label(root)
ysb = tk.Scrollbar(root, orient="vertical", command=canvas.yview)
xsb = tk.Scrollbar(root, orient="horizontal", command=canvas.xview)
canvas.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set)

root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)

canvas.grid(row=0, column=0, sticky="ew")
xsb.grid(row=1, column=0, sticky="ew")
ysb.grid(row=0, column=1, sticky="ns")
label.grid(row=2, columnspan=2, sticky="ew")

x = 10
y = 10
for color in ("red", "orange", "yellow", "green", "blue", "black", "bisque"):
    canvas.create_rectangle(x, y, x+100, y+100,
                            outline=color, fill=color,
                            tags=(color,)
    )
    x += 50
    y += 50
canvas.configure(scrollregion=canvas.bbox("all"))
canvas.bind("<Button-1>", callback)

root.mainloop()

【讨论】:

他们可能正在点击顶部窗口图像下方的窗口图像。很难用语言表达,所以我会用监视器上的图像(窗口)的图像(也是图像)更新问题.. @WinEunuuchs2Unix:我不明白你的评论。在一个像素下点击是不可能的。单击时,鼠标将悬停在一个像素上。无论图像绘制该像素是什么 current 将返回。请编辑您的问题以包含创建多个图像(或更简单的矩形)的minimal reproducible example。 event.widget.find_withtag("current") 还返回项目编号 1,就好像返回图像周围矩形的项目编号而不是图像项目编号一样?至于代码,我可以简单地发布整个内容吗?不知道如何将其切成小块。如果重要,代码在 Linux 中? @WinEunuuchs2Unix:你不需要调用find_withtag("current") - 你可以直接使用"current" 任何你会使用对象ID的地方。注意:如果您在图像周围画一个矩形,然后单击该矩形,current 将代表该矩形。 在程序的早期版本中,当我调整画布大小时,图像不会调整大小,但图像周围的矩形(我没有硬编码)会调整大小。这让我觉得 tkinter 为图像保留了一个项目编号,并为它正在调整大小的图像周围的矩形保留了一个单独的项目编号。请注意,我宁愿只使用一个矩形来表示桌面上的一个窗口,但 alpha 通道(用于透明度)仅适用于 PhotoImage。【参考方案2】:

这是使其工作的最终解决方案:

def popup(event):
    global FoundItem
#    print( "Root x, y: ", event.x_root, event.y_root )
#    print( "event x, y: ", event.x, event.y )
    FoundItem=mycanvas.find_closest(event.x, event.y)
    menu.tk_popup(event.x_root, event.y_root)

您可以删除 cmets(# 字符)以在终端中查看 x,y 坐标。

.tk_popup.post 命令需要屏幕桌面坐标:(event.x_root, event.y_root) .find_closest 命令需要画布坐标:(event.x, event.y)

【讨论】:

以上是关于Python 在坐标处找不到 tkinter 项目的主要内容,如果未能解决你的问题,请参考以下文章

Python Tkinter:如何让画布每帧更新多边形坐标? [解决了]

python tkinter坐标转换

Python升级找不到Tkinter模块

Python升级提示Tkinter模块找不到的解决方法

在 Tkinter Python 程序中看不到图片

Python Tkinter之布局