ログファイルからエントリを解析し、Tkinterを使用してエントリの内容を表示するためにPythonを使用しています。これまでは優れていました。スクロールバーを追加したいのですが、これは非常に簡単なはずですが、わかりません。
ドキュメントは、List、Textbox、Canvas、およびEntryウィジェットのみがスクロールバーインターフェイスをサポートすることを暗示しています。これらのいずれも、ウィジェットのグリッドを表示するのに適しているようには見えません。 Canvasウィジェットに任意のウィジェットを配置することは可能ですが、絶対座標を使用する必要があるように見えるため、グリッドレイアウトマネージャーを使用できませんか?
私はウィジェットグリッドをフレームに入れようとしましたが、それはスクロールバーインターフェースをサポートしていないようですので、これは機能しません:
mainframe = Frame(root, yscrollcommand=scrollbar.set)
誰でもこの制限を回避する方法を提案できますか?スクロールバーを追加するだけで、PyQtで書き直して実行可能イメージのサイズをこれほど大きくする必要はありません。
スクロールバーはいくつかのウィジェットにしか関連付けることができず、ルートウィジェットとFrame
はそのウィジェットグループの一部ではありません。
最も一般的な解決策は、キャンバスウィジェットを作成し、スクロールバーをそのウィジェットに関連付けることです。次に、そのキャンバスに、ラベルウィジェットを含むフレームを埋め込みます。フレームの幅/高さを決定し、それをキャンバスのscrollregion
オプションにフィードして、スクロール領域がフレームのサイズと正確に一致するようにします。
キャンバスにテキストアイテムを直接描画するのはそれほど難しくないため、キャンバスにフレームを埋め込むソリューションが複雑すぎると思われる場合は、このアプローチを再検討する必要があります。グリッドを作成しているので、各テキストアイテムの座標は、特に各行が同じ高さの場合(おそらく単一のフォントを使用している場合)、非常に簡単に計算できます。
キャンバスに直接描画するには、使用しているフォントの行の高さを把握するだけです(それにコマンドがあります)。そして、各y座標はrow*(lineheight+spacing)
です。 x座標は、各列の最も幅の広いアイテムに基づく固定数になります。すべての列にタグを付けると、1つのコマンドで列内のすべてのアイテムのx座標と幅を調整できます。
オブジェクト指向のアプローチを使用した、フレームに埋め込まれたキャンバスソリューションの例を次に示します。
import tkinter as tk
class Example(tk.Frame):
def __init__(self, root):
tk.Frame.__init__(self, root)
self.canvas = tk.Canvas(root, borderwidth=0, background="#ffffff")
self.frame = tk.Frame(self.canvas, background="#ffffff")
self.vsb = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side="right", fill="y")
self.canvas.pack(side="left", fill="both", expand=True)
self.canvas.create_window((4,4), window=self.frame, anchor="nw",
tags="self.frame")
self.frame.bind("<Configure>", self.onFrameConfigure)
self.populate()
def populate(self):
'''Put in some fake data'''
for row in range(100):
tk.Label(self.frame, text="%s" % row, width=3, borderwidth="1",
relief="solid").grid(row=row, column=0)
t="this is the second column for row %s" %row
tk.Label(self.frame, text=t).grid(row=row, column=1)
def onFrameConfigure(self, event):
'''Reset the scroll region to encompass the inner frame'''
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
if __== "__main__":
root=tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()
オブジェクトを使用しないソリューションは次のとおりです。
import tkinter as tk
def populate(frame):
'''Put in some fake data'''
for row in range(100):
tk.Label(frame, text="%s" % row, width=3, borderwidth="1",
relief="solid").grid(row=row, column=0)
t="this is the second column for row %s" %row
tk.Label(frame, text=t).grid(row=row, column=1)
def onFrameConfigure(canvas):
'''Reset the scroll region to encompass the inner frame'''
canvas.configure(scrollregion=canvas.bbox("all"))
root = tk.Tk()
canvas = tk.Canvas(root, borderwidth=0, background="#ffffff")
frame = tk.Frame(canvas, background="#ffffff")
vsb = tk.Scrollbar(root, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((4,4), window=frame, anchor="nw")
frame.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
populate(frame)
root.mainloop()
注: python 2.xでこの作業を行うには、importステートメントでTkinter
ではなくtkinter
を使用します
この便利なクラスを使用して、ウィジェットを含むフレームをスクロール可能にします。次の手順を実行します:
import tkinter as tk
from tkinter import ttk
class Scrollable(tk.Frame):
"""
Make a frame scrollable with scrollbar on the right.
After adding or removing widgets to the scrollable frame,
call the update() method to refresh the scrollable area.
"""
def __init__(self, frame, width=16):
scrollbar = tk.Scrollbar(frame, width=width)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y, expand=False)
self.canvas = tk.Canvas(frame, yscrollcommand=scrollbar.set)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=self.canvas.yview)
self.canvas.bind('<Configure>', self.__fill_canvas)
# base class initialization
tk.Frame.__init__(self, frame)
# assign this obj (the inner frame) to the windows item of the canvas
self.windows_item = self.canvas.create_window(0,0, window=self, anchor=tk.NW)
def __fill_canvas(self, event):
"Enlarge the windows item to the canvas width"
canvas_width = event.width
self.canvas.itemconfig(self.windows_item, width = canvas_width)
def update(self):
"Update the canvas and the scrollregion"
self.update_idletasks()
self.canvas.config(scrollregion=self.canvas.bbox(self.windows_item))
root = tk.Tk()
header = ttk.Frame(root)
body = ttk.Frame(root)
footer = ttk.Frame(root)
header.pack()
body.pack()
footer.pack()
ttk.Label(header, text="The header").pack()
ttk.Label(footer, text="The Footer").pack()
scrollable_body = Scrollable(body, width=32)
for i in range(30):
ttk.Button(scrollable_body, text="I'm a button in the scrollable frame").grid()
scrollable_body.update()
root.mainloop()