python - texto - tkinter: ¿Cómo crear un cuadro de lista que puede aparecer y desaparecer sobre otro widget sin mover el otro widget?
tkinter 8.5 reference a gui for python español (1)
Si desea colocar un widget de manera que no afecte a otros widgets, debe usar el place
. Una de las cosas buenas del place
es que permite ubicar widgets en relación con otros widgets y establecer el ancho y la altura relativos a otros widgets.
Por ejemplo, para colocar la ventana del cuadro de lista (incluido el marco que contiene) inmediatamente debajo del widget que lo activó, puede usar un place
como este:
self.lbframe.place(in_=event.widget, x=0, rely=1, relwidth=1.0, anchor="nw")
La opción relwidth
indica que queremos que el cuadro que contiene el listbox sea exactamente igual de ancho que el widget de entrada. Esto es opcional, pero ilustra el tipo de control que tiene con el place
. El parámetro in_
especifica en qué widget está relacionado el ancho.
Como el place
no afecta a otros hijos del mismo padre, debe asegurarse de que la ventana principal sea lo suficientemente grande como para mostrar la lista. Como la lista es un widget incrustado, no flotará sobre la ventana. En su caso, eso no sucederá porque la instancia de su App
no llena toda la ventana. La razón por la que no es así es porque no le da ningún peso a ninguna fila o columna en la ventana raíz.
grid
es excelente en lo que hace, pero a veces es la herramienta incorrecta para el trabajo. Por ejemplo, dado que la App
es el único widget dentro de la ventana raíz, usar el pack
significa que puede obtener que complete la ventana con un solo comando. El uso de la grid
requiere al menos tres (dos de los cuales olvidó llamar). Por esa razón, recomiendo usar pack
para poner la App
dentro de la root
. Eso no es necesario para hacer que el place
funcione, simplemente hace que su código sea más fácil de entender.
También recomiendo no tener la App
ser responsable de colocarse en su padre. Puede, y nuevamente, no afectará cómo funciona el place
, pero hará que su código sea más fácil de entender porque no tiene una función ( App.__init__
) responsable de diseñar tanto la ventana principal como sus elementos App.__init__
.
Con todo esto en mente, la siguiente es una solución de trabajo basada en su código. Nota: también hay un error en la forma en que está asignando valores al cuadro de lista, pero corregir eso está más allá del alcance de esta pregunta.
#!/usr/bin/python3
# -*- coding: utf-8 -*-
try:
import tkinter as tk # Python 3 tkinter modules
except ImportError:
import Tkinter as tk # Python 2 tkinter modules
class App(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.toggle = True
self.label1 = tk.Label(self, text = ''Click me to see Names of Famous Folks'')
self.label2 = tk.Label(self, text = ''The World of Famous Folks! Welcome!'')
self.label1.grid(row=0, column=0, sticky=''nsew'', pady=10)
self.label2.grid(row=2, column=0, sticky=''nsew'')
nlist1 = [''Peter'', ''Scotty'', ''Walter'', ''Scott'', ''Mary'']
self.lbframe = tk.Frame(self)
self.lbframe.list_values = tk.StringVar()
self.lbframe.list_values.set(nlist1)
self.lbframe.list= tk.Listbox(self.lbframe, height=5, width= 10,
listvariable=self.lbframe.list_values)
self.lbframe.list.pack(fill="both", expand=True)
self.label1.bind("<Button-1>", self.ShowHideListbox)
def ShowHideListbox(self, event):
if self.toggle: # Show
self.toggle=False
self.lbframe.place(in_=event.widget, x=0, rely=1, relwidth=1.0, anchor="nw")
else: # Hide
self.toggle=True
self.lbframe.place_forget()
if __name__ == ''__main__'':
root = tk.Tk()
root.geometry("400x200")
app = App(root)
app.pack(fill="both", expand=True)
app.mainloop()
Pregunta: Me gustaría crear un cuadro de lista que aparezca y desaparezca debajo de label1, pero no debería afectar la posición de label2, cuando se hace clic en label1. ¿Cómo puede hacerse esto?
Antecedentes: escribí un código de prueba (ver a continuación) para mostrar mi problema. En el primer clic del mouse, aparece el cuadro de lista, pero empuja la etiqueta 2 hacia abajo. En el segundo clic del mouse, la caja de lista se esconde pero la etiqueta2 permanece en una nueva ubicación y no vuelve a la posición original. He intentado usar .grid_forget () pero obtuve el mismo comportamiento. Los clics repetidos del mouse simplemente muestran y ocultan el cuadro de lista.
Creo que no puedo obtener el comportamiento deseado del widget porque el listbox es una cuadrícula en el mismo plano / ventana que los otros widgets. Es así, ¿creo que la ventana de nivel superior es el otro widget que permite que exista un widget fuera del plano? Si es así, ¿cómo hago eso? ¿Cómo colocarlo debajo de label1? Además, una ventana de nivel superior tiene una barra de título de ventana. No quiero eso. ¿Cómo eliminar eso? ¿Mi proceso de pensamiento es correcto para obtener lo que quiero?
#!/usr/bin/python3
# -*- coding: utf-8 -*-
try:
import tkinter as tk # Python 3 tkinter modules
except ImportError:
import Tkinter as tk # Python 2 tkinter modules
class App(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.grid(row=0, column=0, sticky=''nsew'')
self.toggle = True
self.label1 = tk.Label(self, text = ''Click me to see Names of Famous Folks'')
self.lbframe = tk.Frame(self)
self.label2 = tk.Label(self, text = ''The World of Famous Folks! Welcome!'')
self.label1.grid(row=0, column=0, sticky=''nsew'', pady=10)
self.lbframe.grid(row=1, column=0, sticky=''nsew'')
self.label2.grid(row=2, column=0, sticky=''nsew'')
nlist1 = [''Peter'', ''Scotty'', ''Walter'', ''Scott'', ''Mary'']
self.lbframe.list_values = tk.StringVar()
self.lbframe.list_values.set(nlist1)
self.lbframe.list= tk.Listbox(self.lbframe, height=5, width= 10,
listvariable=self.lbframe.list_values)
self.lbframe.list.grid(row=0, column=0, sticky=''nsew'')
self.lbframe.list.grid_remove()
self.label1.bind("<Button-1>", self.ShowHideListbox)
def ShowHideListbox(self, event):
if self.toggle: # Show
self.toggle=False
self.lbframe.list.grid()
self.lbframe.list.lift(aboveThis=None)
else: # Hide
self.toggle=True
self.lbframe.list.grid_remove()
if __name__ == ''__main__'':
root = tk.Tk()
app = App(root)
app.mainloop()