英文:
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)))
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论