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

Tkinter Canvas滚动后点击文本无法获取正确值的问题

解决Tkinter Canvas滚动后点击文本获取错误值的问题

问题原因

你用scan_mark/scan_dragto实现的滚动是画布视图平移,并没有改变文本元素的实际坐标。点击时直接用event.x/event.y(视图坐标)调用find_closest,会匹配到原始坐标下该位置的元素,而非你实际点击的、被滚动后的元素。

修复步骤

  1. 转换点击坐标:用canvas.canvasx(event.x)和canvas.canvasy(event.y)将视图坐标转换为画布的原始坐标,这样find_closest就能找到正确的元素。
  2. 直接获取文本内容:无需通过字典反向查找,用canvas.itemcget(item_id, "text")直接获取文本元素的内容,更简洁可靠。
  3. 优化冗余代码:移除重复定义的字典,减少全局变量依赖。

修复后的完整代码

import tkinter as tk
import customtkinter

root = tk.Tk()
root.config(bg='#ffffff')
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (w, h))
root.eval('tk::PlaceWindow %s center' % root.winfo_pathname(root.winfo_id()))
root.resizable(True, True)


def Example():
    atras_button2 = None

    def optionmenu_callback():
        nonlocal atras_button2

        if atras_button2:
            atras_button2.destroy()

        def scroll_start(event):
            canvas.scan_mark(event.x, event.y)

        def scroll_move(event):
            canvas.scan_dragto(event.x, event.y, gain=2)

        canvasmaster = tk.Canvas(root, width=252, height=512, background="#ffffff")
        canvasmaster.place(relx=0.428, rely=0.48, anchor="center")

        canvas = tk.Canvas(master=canvasmaster, width=250, height=350, background="white")
        canvas.configure(scrollregion=(0, 0 ,250,1000))
        canvas.grid(row=0, column=0, sticky="nsew")

        def onObjectClick(event):   
            nonlocal atras_button2
            # 转换坐标为画布原始坐标
            x = canvas.canvasx(event.x)
            y = canvas.canvasy(event.y)
            # 找到正确的元素
            item_id = canvas.find_closest(x, y)[0]
            # 直接获取文本内容
            prefix = canvas.itemcget(item_id, "text")
            print("prefijo", prefix)
            
            textprefijo_var = customtkinter.StringVar(value=prefix)
            textprefijo = customtkinter.CTkLabel(
                master=root, 
                textvariable=textprefijo_var, 
                anchor="w", 
                padx=4, 
                font=customtkinter.CTkFont(family="Lato", size=18),  
                text_color="#333333"
            )
            textprefijo.place(relx=0.374, rely=0.20, anchor="w")

            canvas.destroy()
            canvasmaster.destroy()

            atras_button2 = customtkinter.CTkButton(
                master=root, 
                corner_radius=8, 
                height=60, 
                border_spacing=1, 
                border_color="#385974", 
                border_width=2, 
                text="ATRÁS", 
                font=customtkinter.CTkFont(family="Roboto", size=20), 
                fg_color="#ffffff", 
                text_color="#385974", 
                hover_color=("#ffffff", "gray10"), 
                width=60,  
                anchor="center",  
                command=optionmenu_callback
            )
            atras_button2.place(relx=0.515, rely=0.20, anchor="center")

        # 只定义一次字典
        diccionariodepaises = {
            'España':'1','Afganistán':'2','Albania':'3','Alemania':'4','Angola':'5','Anguila':'6',
            'Antigua y Barbuda':'7','Antillas Holandesas':'8','Arabia Saudita':'9','Argelia':'10',
            'Argentina':'11','Armenia':'12','Aruba':'13','Australia':'14','Austria':'15','Azerbaiyán':'16',
            'Bahamas':'17','Bangladés':'18','Barbados':'19','Bélgica':'20','Belice':'21','Benín':'22',
            'Bermudas':'23','Bielorrusia':'24','Bolivia':'25','Bosnia y Herzegovina':'26','Botsuana':'27',
            'Brasil':'28','Brunéi':'29','Bulgaria':'30','Burkina Faso':'31','Burundi':'32'
        }
        
        for idx, (country, _) in enumerate(diccionariodepaises.items()):
            canvas.create_text(10, (idx+1)*30, anchor="w", text=country, tags=str(idx+1))
            canvas.tag_bind(str(idx+1), '<ButtonPress-1>', onObjectClick)

        canvas.bind("<ButtonPress-1>", scroll_start)
        canvas.bind("<B1-Motion>", scroll_move)


    Entry_textoEMAILreservaBOX= customtkinter.CTkEntry(
        master=root, 
        width=250, 
        height=60, 
        font=customtkinter.CTkFont(family="Lato", size=10), 
        justify='center', 
        placeholder_text_color="#003f5c", 
        fg_color="#ffffff"
    )
    Entry_textoEMAILreservaBOX.place(relx=0.428, rely=0.20, anchor="center")
    Entry_textoEMAILreservaBOX.configure(justify="left", state="disabled")

    atras_button2 = customtkinter.CTkButton(
        master=root, 
        corner_radius=8, 
        height=60, 
        border_spacing=1, 
        border_color="#385974", 
        border_width=2, 
        text="ATRÁS", 
        font=customtkinter.CTkFont(family="Roboto", size=20), 
        fg_color="#ffffff", 
        text_color="#385974", 
        hover_color=("#ffffff", "gray10"), 
        width=60,  
        anchor="center",  
        command=optionmenu_callback
    )
    atras_button2.place(relx=0.515, rely=0.20, anchor="center")


if __name__ == "__main__":
    Example()
    root.mainloop()

关键修改点

  • 用canvas.canvasx()和canvasy()转换点击坐标,解决滚动后坐标不匹配的问题。
  • 用canvas.itemcget(item_id, "text")直接获取文本,避免字典反向查找的冗余和出错可能。
  • 用nonlocal替代全局变量,减少全局作用域污染。
  • 循环遍历字典时用enumerate,代码更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 00:12:32