J'utilise Python pour analyser les entrées d'un fichier journal et afficher le contenu des entrées à l'aide de Tkinter et jusqu'à présent, il a été excellent. La sortie est une grille de widgets d'étiquettes, mais parfois il y a plus de lignes que ce qui peut être affiché à l'écran. Je voudrais ajouter une barre de défilement, qui semble être très facile, mais je ne peux pas le comprendre.
La documentation implique que seuls les widgets List, Textbox, Canvas et Entry prennent en charge l'interface de la barre de défilement. Aucun de ceux-ci ne semble approprié pour afficher une grille de widgets. Il est possible de mettre des widgets arbitraires dans un widget Canvas, mais vous semblez devoir utiliser des coordonnées absolues, donc je ne pourrais pas utiliser le gestionnaire de disposition de grille?
J'ai essayé de mettre la grille de widgets dans un cadre, mais cela ne semble pas prendre en charge l'interface de la barre de défilement, donc cela ne fonctionne pas:
mainframe = Frame(root, yscrollcommand=scrollbar.set)
Quelqu'un peut-il suggérer un moyen de contourner cette limitation? Je détesterais devoir réécrire dans PyQt et augmenter la taille de mon image exécutable de tant, juste pour ajouter une barre de défilement!
Vous ne pouvez associer des barres de défilement qu'à quelques widgets, et le widget racine et Frame
ne font pas partie de ce groupe de widgets.
La solution la plus courante consiste à créer un widget de canevas et à associer les barres de défilement à ce widget. Ensuite, dans cette toile, incorporez le cadre qui contient vos widgets d'étiquette. Déterminez la largeur/hauteur du cadre et introduisez-la dans le canevas scrollregion
afin que la région de défilement corresponde exactement à la taille du cadre.
Il n'est pas très difficile de dessiner les éléments de texte directement sur le canevas, vous voudrez peut-être reconsidérer cette approche si la solution frame-embedded-in-a-canvas semble trop complexe. Puisque vous créez une grille, les coordonnées de chaque élément de texte vont être très faciles à calculer, surtout si chaque ligne a la même hauteur (ce qui est probablement le cas si vous utilisez une seule police).
Pour dessiner directement sur la toile, déterminez simplement la hauteur de ligne de la police que vous utilisez (et il existe des commandes pour cela). Ensuite, chaque coordonnée y est row*(lineheight+spacing)
. La coordonnée x sera un nombre fixe basé sur l'élément le plus large de chaque colonne. Si vous donnez à chaque élément une balise pour la colonne dans laquelle il se trouve, vous pouvez ajuster la coordonnée x et la largeur de tous les éléments d'une colonne avec une seule commande.
Voici un exemple de la solution frame-embedded-in-canvas, utilisant une approche orientée objet:
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()
Voici une solution qui n'utilise pas d'objets:
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()
Remarque: pour que cela fonctionne dans python 2.x, utilisez Tkinter
plutôt que tkinter
dans l'instruction d'importation
Utilisez cette classe pratique pour faire défiler le cadre contenant vos widgets. Suivez ces étapes:
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()