【问题标题】:Tkinter / TTK - Prevent string to ButtonPress conversionTkinter / TTK - 防止字符串到 ButtonPress 转换
【发布时间】:2017-09-30 18:55:50
【问题描述】:

我正在编写一个简单的脚本来创建一个 ttk Treeview(充当表格),当您双击它时,它会打开一个文件(路径保存在字典中)。但是,当您双击一行时,您会收到此错误:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\Maicol\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py",
line 1699, in __call__
    return self.func(*args)
  File "C:\Users\Maicol\Documents\Projects\App_WINDOWS\School_Life_Diary\note.py",
line 195, in <lambda>
    lambda f=nt[x]["URIallegato"]: os.startfile(str(f)))
FileNotFoundError: [WinError 2] Can't find the specified file: '<ButtonPress event state=Mod1 num=1 x=677 y=37>'

问题是这段代码:

t.bind("<Double-1>", lambda f=nt[x]["URIallegato"]: os.startfile(str(f)))

允许双击打开文件。

这是完整的Treeview 代码:

t=Treeview(w)
t.pack(padx=10,pady=10)
for x in list(nt.keys()):
    t.insert("",x,text=nt[x]["allegati"])
    if nt[x]["allegati"]!="":
        t.bind("<Double-1>",
               lambda f=nt[x]["URIallegato"]: os.startfile(str(f)))

【问题讨论】:

    标签: python tkinter treeview ttk


    【解决方案1】:

    主要问题是在循环中为Treeview 创建绑定。

    只有一个双击事件可以为树声明和触发,而不是逐行声明,在这里你在每次迭代中都覆盖它。


    众所周知,这种 lambda 模式会为 for/loop 中的小部件声明 命令,它可以很好地用于此目的:

    lambda f=nt[x]["URIallegato"]: os.startfile(str(f))
    

    但是在这里你声明了一个默认参数f,并且lambda 将使用事件绑定给出的event 参数执行,这就是你在异常中得到的:&lt;ButtonPress event state=Mod1...

    无论如何,我们发现这在您的情况下不起作用,即使您使用第二个参数修复 lambda 以接受 event 而不替换您的默认值 f


    我的建议是使用每行的values 字段来存储信息URIallegato",而不在树中显示列。

    然后您可以将通用事件绑定到 Treeview,通过使用 focus() 来获取选定项,并提取值以获取 URI。

    t=Treeview(w)
    t.pack(padx=10,pady=10)
    
    def open_item(event):
        item = t.item(t.focus())
        if item['text']:
            os.startfile(item['values'][0])
    
    for x in list(nt.keys()):
        value = ''
        if nt[x]["allegati"]:
            value = str(nt[x]["URIallegato"])
        t.insert("",x,text=nt[x]["allegati"], values=value)
    
    t.bind("<Double-1>", open_item)
    

    如果要检查是否有要打开的 URI,这里几乎不能使用 lambda。

    【讨论】:

      【解决方案2】:

      当事件触发时,tkinter 将传递一个事件对象。您正尝试像打开文件一样打开该事件对象。

      这是为什么呢?让我们首先将您的 lambda 重写为适当的函数。你的 lambda 相当于这个函数:

      def handle_event(f=default_value):
          os.startfile(str(default_value))
      

      当事件触发时,它会执行与此等价的操作:

      handle_event(event)
      

      您的脚本有一个位置参数event,它被分配给第一个关键字参数。因此fevent 相同。

      解决方案是确保您的 lambda 接受该事件,它可以简单地忽略:

      lambda event, f=nt[x]["URIallegato"]: os.startfile(str(f)))
      

      通过上述方式,event 对象将与 event 参数相关联,f 的默认值将作为 f 传递。

      【讨论】:

      • 我也是这样做的,但似乎所有绑定都被最后一个值覆盖了?
      • @PRMoureu:我不明白你的评论。创建绑定时,该函数将自动传递一个位置参数。如何解释是基于 python 的标准规则。
      • 可以,但是事件是在循环中绑定到Treeview的,那么不同的行会触发什么事件呢?
      • @PRMoureu:我不明白你的问题。双击时触发该事件。我不知道您所说的“在不同的行中”是什么意思。代码行?树视图小部件中的行(行)?
      • 不,我的意思是...每一行都有一个 ID,通过这个 ID,我可以(通过预编译的字典)获取要打开的文件的路径。当我单击一行时,它会打开插入的最新行的文件。我想要的是为任何行打开不同的文件。有可能吗?
      猜你喜欢
      • 2014-12-21
      • 2021-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多