単一のスクロールバーを使用して一緒にスクロールする複数のTkinterリストボックスがありますが、リストボックスのいずれかでマウスホイールのアクティビティのためにそれらを一緒にスクロールしたいです。
これを行う方法?
私の現在のコードは、ここで説明した最後のパターンに基づいています: http://effbot.org/tkinterbook/listbox.htm スクロールバーのみを使用する場合は問題なく動作しますが、マウスホイールを使用するとリストボックスが個別にスクロールします。
単一のスクロールバーを使用して一緒にスクロールする複数のTkinterリストボックスがありますが、リストボックスのいずれかでマウスホイールのアクティビティのためにそれらを一緒にスクロールしたいです。
これを行う方法?
私の現在のコードは、ここで説明した最後のパターンに基づいています: http://effbot.org/tkinterbook/listbox.htm スクロールバーのみを使用する場合は問題なく動作しますが、マウスホイールを使用するとリストボックスが個別にスクロールします。
これがかなり古いことは知っていますが、解決策はここで提案されているものよりも少し簡単だと思います。リストボックスを常に一致させたいと仮定すると、上記の2つの答えは完全な解決策ではありません-矢印キーを使用して選択を変更すると、一方のリストボックスはスクロールされますが、もう一方はスクロールされません.
それで、答えを見て、私は尋ねました-スクロールバーに直接送信するのではなく、なぜyscrollcommandコールバックをフックしないのですか? だから、私はまさにそれをしました:
try:
from Tkinter import *
except ImportError:
from tkinter import *
class MultipleScrollingListbox(Tk):
def __init__(self):
Tk.__init__(self)
self.title('Scrolling Multiple Listboxes')
#the shared scrollbar
self.scrollbar = Scrollbar(self, orient='vertical')
#note that yscrollcommand is set to a custom method for each listbox
self.list1 = Listbox(self, yscrollcommand=self.yscroll1)
self.list1.pack(fill='y', side='left')
self.list2 = Listbox(self, yscrollcommand=self.yscroll2)
self.list2.pack(expand=1, fill='both', side='left')
self.scrollbar.config(command=self.yview)
self.scrollbar.pack(side='right', fill='y')
#fill the listboxes with stuff
for x in xrange(30):
self.list1.insert('end', x)
self.list2.insert('end', x)
#I'm sure there's probably a slightly cleaner way to do it than this
#Nevertheless - whenever one listbox updates its vertical position,
#the method checks to make sure that the other one gets updated as well.
#Without the check, I *think* it might recurse infinitely.
#Never tested, though.
def yscroll1(self, *args):
if self.list2.yview() != self.list1.yview():
self.list2.yview_moveto(args[0])
self.scrollbar.set(*args)
def yscroll2(self, *args):
if self.list1.yview() != self.list2.yview():
self.list1.yview_moveto(args[0])
self.scrollbar.set(*args)
def yview(self, *args):
self.list1.yview(*args)
self.list2.yview(*args)
if __name__ == "__main__":
root = MultipleScrollingListbox()
root.mainloop()
2 つのウィジェットを 1 つのスクロールバーに接続するのとほぼ同じ方法で問題を解決します。マウスホイール用のカスタム バインディングを作成し、それらのバインディングが 1 つだけではなく両方のリストボックスに影響するようにします。
唯一の本当のトリックは、プラットフォームに応じてマウスホイールに対して異なるイベントを取得することを知っていることです: Windows と Mac は<MouseWheel>
イベントを取得し、Linux はイベントを取得<Button-4>
し<Button-5>
ます。
Python 2.5 を使用して私の Mac でテストした例を次に示します。
import Tkinter as tk
class App:
def __init__(self):
self.root=tk.Tk()
self.vsb = tk.Scrollbar(orient="vertical", command=self.OnVsb)
self.lb1 = tk.Listbox(self.root, yscrollcommand=self.vsb.set)
self.lb2 = tk.Listbox(self.root, yscrollcommand=self.vsb.set)
self.vsb.pack(side="right",fill="y")
self.lb1.pack(side="left",fill="x", expand=True)
self.lb2.pack(side="left",fill="x", expand=True)
self.lb1.bind("<MouseWheel>", self.OnMouseWheel)
self.lb2.bind("<MouseWheel>", self.OnMouseWheel)
for i in range(100):
self.lb1.insert("end","item %s" % i)
self.lb2.insert("end","item %s" % i)
self.root.mainloop()
def OnVsb(self, *args):
self.lb1.yview(*args)
self.lb2.yview(*args)
def OnMouseWheel(self, event):
self.lb1.yview("scroll", event.delta,"units")
self.lb2.yview("scroll",event.delta,"units")
# this prevents default bindings from firing, which
# would end up scrolling the widget twice
return "break"
app=App()
これが私の現在のソリューションで、スタンドアロン関数としてコード化されています (はい、オブジェクトである必要があります)。
機能/要件:
コード:
def showLists(l, *lists):
"""
Present passed equal-length lists in adjacent scrollboxes.
"""
# This exists mainly for me to start learning about Tkinter.
# This widget reqires at least one list be passed, and as many additional
# lists as desired. Each list is displayed in its own listbox, with
# additional listboxes added to the right as needed to display all lists.
# The width of each listbox is set to match the max width of its contents.
# Caveat: Too wide or too many lists, and the widget can be wider than the screen!
# The listboxes scroll together, using either the scrollbar or mousewheel.
# :TODO: Refactor as an object with methods.
# :TODO: Move to a separate file when other widgets are built.
# Check arguments
if (l is None) or (len(l) < 1):
return
listOfLists = [l] # Form a list of lists for subsequent processing
listBoxes = [] # List of listboxes
if len(lists) > 0:
for list in lists:
# All lists must match length of first list
# :TODO: Add tail filling for short lists, with error for long lists
if len(list) != len(l):
return
listOfLists.append(list)
import Tkinter
def onVsb(*args):
"""
When the scrollbar moves, scroll the listboxes.
"""
for lb in listBoxes:
lb.yview(*args)
def onMouseWheel(event):
"""
Convert mousewheel motion to scrollbar motion.
"""
if (event.num == 4): # Linux encodes wheel as 'buttons' 4 and 5
delta = -1
elif (event.num == 5):
delta = 1
else: # Windows & OSX
delta = event.delta
for lb in listBoxes:
lb.yview("scroll", delta, "units")
# Return 'break' to prevent the default bindings from
# firing, which would end up scrolling the widget twice.
return "break"
# Create root window and scrollbar
root = Tkinter.Tk()
root.title('Samples w/ time step < 0')
vsb = Tkinter.Scrollbar(root, orient=Tkinter.VERTICAL, command=onVsb)
vsb.pack(side=Tkinter.RIGHT, fill=Tkinter.Y)
# Create listboxes
for i in xrange(0,len(listOfLists)):
lb = Tkinter.Listbox(root, yscrollcommand=vsb.set)
lb.pack(side=Tkinter.LEFT, fill=Tkinter.BOTH)
# Bind wheel events on both Windows/OSX & Linux;
lb.bind("<MouseWheel>", onMouseWheel)
lb.bind("<Button-4>", onMouseWheel)
lb.bind("<Button-5>", onMouseWheel)
# Fill the listbox
maxWidth = 0
for item in listOfLists[i]:
s = str(item)
if len(s) > maxWidth:
maxWidth = len(s)
lb.insert(Tkinter.END, s)
lb.config(width=maxWidth+1)
listBoxes.append(lb) # Add listbox to list of listboxes
# Show the widget
Tkinter.mainloop()
# End of showLists()
改善のための提案は大歓迎です!