英文:
Why does tkinter PanedWindow.sash_place not work in bind_class?
问题
I wanted to customize the way the sash on a tkinter PanedWindow looked based on if the user is clicking on it, so I cam up with this.
import tkinter as tk
from tkinter import ttk
class PanedTest(tk.Tk):
def __init__(self, master=None):
super().__init__(master)
self.style = ttk.Style()
self.style.configure('TPanedwindow', background='black')
self.style.configure('Clicked.TPanedwindow', background='blue')
self.style.configure('Sash', sashthickness=8)
self.bind_class('TPanedwindow', '<Button-1>', lambda e: e.widget.configure(style='Clicked.TPanedwindow'))
self.bind_class('TPanedwindow', '<B1-Motion>', lambda e: e.widget.sash_place(0, e.x, e.y))
self.bind_class('TPanedwindow', '<ButtonRelease-1>', lambda e: e.widget.configure(style='TPanedwindow'))
self.paned1 = ttk.PanedWindow()
self.paned1.pack()
self.frame1 = ttk.Frame(self.paned1, width=200, height=200)
self.paned1.add(self.frame1)
self.frame2 = ttk.Frame(self.paned1, width=200, height=200)
self.paned1.add(self.frame2)
self.paned2 = ttk.PanedWindow(orient='horizontal')
self.paned2.pack()
self.frame3 = ttk.Frame(self.paned2, width=200, height=200)
self.paned2.add(self.frame3)
self.frame4 = ttk.Frame(self.paned2, width=200, height=200)
self.paned2.add(self.frame4)
if __name__ == "__main__":
app = PanedTest()
app.mainloop()
Unfortunately, this results in an error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 1921, in __call__
return self.func(*args)
File "paned_test.py", line 15, in <lambda>
self.bind_class('TPanedwindow', '<B1-Motion>', lambda e: e.widget.sash_place(0, e.x, e.y))
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 4490, in sash_place
return self.sash("place", index, x, y)
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 4464, in sash
self.tk.call((self._w, 'sash') + args)) or ()
_tkinter.TclError: wrong # args: should be ".!panedwindow2 sashpos index ?newpos?"
So after a lot of testing, I managed to get it to work with this:
import tkinter as tk
from tkinter import ttk
class PanedTest(tk.Tk):
def __init__(self, master=None):
super().__init__(master)
self.style = ttk.Style()
self.style.configure('TPanedwindow', background='black')
self.style.configure('Clicked.TPanedwindow', background='blue')
self.style.configure('Sash', sashthickness=8)
self.bind_class('TPanedwindow', '<Button-1>', lambda e: e.widget.configure(style='Clicked.TPanedwindow'))
self.bind_class('TPanedwindow', '<B1-Motion>', lambda e: e.widget.sash(0, (e.x if str(e.widget.cget('orient')) == 'horizontal' else e.y)))
self.bind_class('TPanedwindow', '<ButtonRelease-1>', lambda e: e.widget.configure(style='TPanedwindow'))
self.paned1 = ttk.PanedWindow()
self.paned1.pack()
self.frame1 = ttk.Frame(self.paned1, width=200, height=200)
self.paned1.add(self.frame1)
self.frame2 = ttk.Frame(self.paned1, width=200, height=200)
self.paned1.add(self.frame2)
self.paned2 = ttk.PanedWindow(orient='horizontal')
self.paned2.pack()
self.frame3 = ttk.Frame(self.paned2, width=200, height=200)
self.paned2.add(self.frame3)
self.frame4 = ttk.Frame(self.paned2, width=200, height=200)
self.paned2.add(self.frame4)
if __name__ == "__main__":
app = PanedTest()
app.mainloop()
I can now drag the paned windows, but it does also give this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 1921, in __call__
return this.func(*args)
File "paned_test.py", line 48, in <lambda>
self.bind_class('TPanedwindow', '<B1-Motion>', lambda e: e.widget.sash(0, (e.x if str(e.widget.cget('orient')) == 'horizontal' else e.y)))
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 4463, in sash
return self._getints(
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 1467, in _getints
return tuple(map(self.tk.getint, self.tk.splitlist(string)))
TypeError: splitlist() argument must be str, bytes or bytearray, not int
Here's the difference between the two scripts:
```python
self.bind_class('TPanedwindow', '<B1-Motion>', lambda e: e.widget.sash_place(0, e.x, e.y))
vs
self.bind_class('TPanedwindow', '<B1-Motion>', lambda e: e.widget.sash(0, (e.x if str(e.widget.cget('orient')) == 'horizontal' else e.y)))
Why does only the second one work, and not the function that is specifically for what I am trying to do?
Oh, and converting e.widget.cget('orient') to str is just because the result is a <class '_tkinter.Tcl_Obj'>.
英文:
I wanted to customize the way the sash on a tkinter PanedWindow looked based on if the user is clicking on it, so I cam up with this.
import tkinter as tk
from tkinter import ttk
class PanedTest(tk.Tk):
def __init__(self, master = None) -> None:
super().__init__(master)
self.style = ttk.Style()
self.style.configure('TPanedwindow', background='black')
self.style.configure('Clicked.TPanedwindow', background='blue')
self.style.configure('Sash', sashthickness = 8)
self.bind_class('TPanedwindow', '<Button-1>', lambda e : e.widget.configure(style = 'Clicked.TPanedwindow'))
self.bind_class('TPanedwindow', '<B1-Motion>', lambda e : e.widget.sash_place(0, e.x, e.y))
self.bind_class('TPanedwindow', '<ButtonRelease-1>', lambda e : e.widget.configure(style = 'TPanedwindow'))
self.paned1 = ttk.PanedWindow()
self.paned1.pack()
self.frame1 = ttk.Frame(self.paned1, width = 200, height = 200)
self.paned1.add(self.frame1)
self.frame2 = ttk.Frame(self.paned1, width = 200, height = 200)
self.paned1.add(self.frame2)
self.paned2 = ttk.PanedWindow(orient = 'horizontal')
self.paned2.pack()
self.frame3 = ttk.Frame(self.paned2, width = 200, height = 200)
self.paned2.add(self.frame3)
self.frame4 = ttk.Frame(self.paned2, width = 200, height = 200)
self.paned2.add(self.frame4)
if __name__ == "__main__":
app = PanedTest()
app.mainloop()
Unfortunately, this results in an error
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 1921, in __call__
return self.func(*args)
File "paned_test.py", line 15, in <lambda>
self.bind_class('TPanedwindow', '<B1-Motion>', lambda e : e.widget.sash_place(0, e.x, e.y))
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 4490, in sash_place
return self.sash("place", index, x, y)
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 4464, in sash
self.tk.call((self._w, 'sash') + args)) or ()
_tkinter.TclError: wrong # args: should be ".!panedwindow2 sashpos index ?newpos?"
So after a lot of testing, I managed to get it to work with this
import tkinter as tk
from tkinter import ttk
class PanedTest(tk.Tk):
def __init__(self, master = None) -> None:
super().__init__(master)
self.style = ttk.Style()
self.style.configure('TPanedwindow', background='black')
self.style.configure('Clicked.TPanedwindow', background='blue')
self.style.configure('Sash', sashthickness = 8)
self.bind_class('TPanedwindow', '<Button-1>', lambda e : e.widget.configure(style = 'Clicked.TPanedwindow'))
self.bind_class('TPanedwindow', '<B1-Motion>', lambda e : e.widget.sash(0, (e.x if str(e.widget.cget('orient')) == 'horizontal' else e.y)))
self.bind_class('TPanedwindow', '<ButtonRelease-1>', lambda e : e.widget.configure(style = 'TPanedwindow'))
self.paned1 = ttk.PanedWindow()
self.paned1.pack()
self.frame1 = ttk.Frame(self.paned1, width = 200, height = 200)
self.paned1.add(self.frame1)
self.frame2 = ttk.Frame(self.paned1, width = 200, height = 200)
self.paned1.add(self.frame2)
self.paned2 = ttk.PanedWindow(orient = 'horizontal')
self.paned2.pack()
self.frame3 = ttk.Frame(self.paned2, width = 200, height = 200)
self.paned2.add(self.frame3)
self.frame4 = ttk.Frame(self.paned2, width = 200, height = 200)
self.paned2.add(self.frame4)
if __name__ == "__main__":
app = PanedTest()
app.mainloop()
I can now drag the paned windows, but it does also give this error
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 1921, in __call__
return self.func(*args)
File "paned_test.py", line 48, in <lambda>
self.bind_class('TPanedwindow', '<B1-Motion>', lambda e : e.widget.sash(0, (e.x if str(e.widget.cget('orient')) == 'horizontal' else e.y)))
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 4463, in sash
return self._getints(
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 1467, in _getints
return tuple(map(self.tk.getint, self.tk.splitlist(string)))
TypeError: splitlist() argument must be str, bytes or bytearray, not int
Here's the difference between the two scripts
self.bind_class('TPanedwindow', '<B1-Motion>', lambda e : e.widget.sash_place(0, e.x, e.y))
vs
self.bind_class('TPanedwindow', '<B1-Motion>', lambda e : e.widget.sash(0, (e.x if str(e.widget.cget('orient')) == 'horizontal' else e.y)))
Why does only the second one work, and not the function that is specifically for what I am trying to do?
oh, and converting e.widget.cget('orient') to str is just because the result is a <class '_tkinter.Tcl_Obj'>.
答案1
得分: 1
这可能是tkinter上的一个错误。对于你的第一个使用e.widget.sash_place(0, e.x, e.y)的代码,它实际上只是内部调用了self.sash('place', index, x, y),因为.sash()只接受两个参数,即index和pos(即你在第二个代码块中使用.sash()的方式)。
然而,在.sash()内部,它对self.tk.call(...)的结果调用了self._getints(),而这个结果是一个整数,但self._getints()期望一个字符串。这就是第二个错误的含义。
要避免第二个错误,你可以直接使用e.widget.tk.call()而不是.sash():
self.bind_class('TPanedwindow', '<B1-Motion>',
lambda e: e.widget.tk.call(e.widget, "sash", 0, (e.x if str(e.widget["orient"]) == "horizontal" else e.y)))
英文:
This may be a bug on tkinter. For your first code that uses e.widget.sash_place(0, e.x, e.y), it just internally calls self.sash('place', index, x, y) which raises the invalid number of arguments error because .sash() expects two arguments, index and pos, only (i.e. how you use .sash() in your second code block.
However inside .sash() it calls self._getints() on the result of self.tk.call(...) which is an integer, but self._getints() expects a string. That is what the second error means.
To avoid the second error, you can use e.widget.tk.call() directly instead of .sash():
self.bind_class('TPanedwindow', '<B1-Motion>',
lambda e: e.widget.tk.call(e.widget, "sash", 0, (e.x if str(e.widget["orient"]) == "horizontal" else e.y)))
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论