python - standard - tkinter button
Agregar una barra de desplazamiento a un grupo de widgets en Tkinter (2)
Hacerlo desplazable
Utiliza esta práctica clase para hacer desplazable el marco que contiene tus widgets. Sigue estos pasos:
- crear el marco
- mostrarlo (paquete, cuadrícula, etc.)
- hacerlo desplazable
- agregar widgets dentro de ella
- llamar al método update ()
import tkinter as tk
from tkinter import ttk
class Scrollable(ttk.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))
Ejemplo de uso
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()
Estoy usando Python para analizar entradas de un archivo de registro y mostrar los contenidos de la entrada usando Tkinter, y hasta ahora ha sido excelente. El resultado es una cuadrícula de widgets de etiquetas, pero a veces hay más filas de las que se pueden mostrar en la pantalla. Me gustaría agregar una barra de desplazamiento, que parece que debería ser muy fácil, pero no puedo resolverlo.
La documentación implica que solo los widgets List, Textbox, Canvas y Entry admiten la interfaz de la barra de desplazamiento. Ninguno de estos parece ser adecuado para mostrar una cuadrícula de widgets. Es posible poner widgets arbitrarios en un widget Canvas, pero parece que tienes que usar coordenadas absolutas, por lo que no podría usar el administrador de diseño de grid.
Intenté colocar la cuadrícula de widgets en un Frame, pero eso no parece ser compatible con la interfaz de la barra de desplazamiento, así que esto no funciona:
mainframe = Frame(root, yscrollcommand=scrollbar.set)
¿Alguien puede sugerir un camino alrededor de esta limitación? ¡Odiaría tener que reescribir en PyQt y aumentar mi tamaño de imagen ejecutable tanto, solo para agregar una barra de desplazamiento!
Visión de conjunto
Solo puede asociar barras de desplazamiento con algunos widgets, y el widget raíz y el Frame
no son parte de ese grupo de widgets.
La solución más común es crear un widget canvas y asociar las barras de desplazamiento con ese widget. Luego, en ese lienzo, incrusta el marco que contiene tus widgets de etiquetas. Determine el ancho / alto del marco e introdúzcalo en la opción scrollregion
modo que la región de desplazamiento coincida exactamente con el tamaño del marco.
Dibujar los elementos de texto directamente en el lienzo no es muy difícil, por lo que es posible que desee reconsiderar ese enfoque si la solución frame-embedded-in-a-canvas parece demasiado compleja. Como está creando una cuadrícula, las coordenadas de cada elemento de texto serán muy fáciles de calcular, especialmente si cada fila tiene la misma altura (lo que probablemente sea si usa una sola fuente).
Para dibujar directamente en el lienzo, solo calcula la altura de la línea de la fuente que estás utilizando (y hay comandos para eso). Luego, cada coordenada y es una row*(lineheight+spacing)
. La coordenada x será un número fijo basado en el elemento más ancho de cada columna. Si le da a todo una etiqueta para la columna en la que se encuentra, puede ajustar la coordenada xy el ancho de todos los elementos en una columna con un solo comando.
Solución orientada a objetos
Aquí hay un ejemplo de la solución frame-embedded-in-canvas, que utiliza un enfoque orientado a objetos:
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 __name__ == "__main__":
root=tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()
Solución de procedimiento
Aquí hay una solución que no usa objetos:
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()
Nota: para que esto funcione en python 2.x, use Tkinter
lugar de tkinter
en la instrucción de importación