2017-01-23 3 views
0

Вопрос: Я хотел бы создать список, который будет отображаться и исчезать под меткой1, но он не должен влиять на положение метки2 при нажатии метки1. Как это может быть сделано?tkinter: Как создать список, который может появляться и исчезать над другим виджетами без перемещения другого виджета?

Фон: Я написал тестовый код (см. Ниже), чтобы показать свою проблему. При первом щелчке мыши появляется список, но нажимает label2 вниз. При втором щелчке мыши скрывается список, но label2 остается в новом месте и не возвращается в исходное положение. Я попытался использовать .grid_forget(), но получил то же поведение. Повторные щелчки мыши просто отображают и скрывают список.

Я думаю, что я не могу получить желаемое поведение виджета, потому что список - это сетка в той же плоскости/окне, что и другие виджеты. Так, я думаю, что окно верхнего уровня - это другой виджет, который позволяет существовать виджет вне плоскости? Если да, то как мне это сделать? Как разместить его ниже label1? Кроме того, окна верхнего уровня имеют строку заголовка окна. Я не хочу этого. Как удалить это? Правильно ли мой процесс мышления, чтобы получить то, что я хочу?

#!/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() 
+0

похоже, что вы пытаетесь имитировать выпадающий список. есть ли причина, по которой вы не используете combobox? –

+0

, если вы хотите переместить 'label2' вверх, затем скройте' lbframe' - 'self.lbframe.grid_forget()', а затем используйте 'self.lbframe.list.grid (row = 1, column = 0, sticky = 'nsew') ', чтобы показать это снова между двумя метками. – furas

ответ

2

Если вы хотите расположить виджет таким образом, что он не влияет на какие-либо другие виджеты, вы должны использовать place. Одна из хороших вещей о place заключается в том, что он позволяет размещать виджеты относительно других виджетов и устанавливать ширину и высоту относительно других виджетов.

Например, чтобы поместить окно ListBox (в том числе содержащего его кадр) непосредственно под виджетом, который вызвал его появляться, вы могли бы использовать place так:

self.lbframe.place(in_=event.widget, x=0, rely=1, relwidth=1.0, anchor="nw") 

Опция relwidth говорит, что мы хотим кадр, содержащий список, чтобы быть таким же широким, как виджет ввода. Это необязательно, но он иллюстрирует тип управления, который у вас есть: place. Параметр in_ указывает, какой виджет имеет ширину относительно.

Поскольку place не влияет на других детей одного и того же родителя, вы должны убедиться, что родительское окно достаточно велико, чтобы отобразить список. Поскольку список - встроенный виджет, он не будет плавать над окном. В вашем случае это не произойдет, потому что ваш экземпляр App не заполняет все окно. Причина в том, что это не так, потому что вы пренебрегаете любыми весами строк или столбцов в корневом окне.

grid отлично подходит к тому, что он делает, но иногда это неправильный инструмент для работы. Например, поскольку является единственным виджетами внутри корневого окна, использование pack означает, что вы можете получить его, чтобы заполнить окно одной командой. Использование grid требует, чтобы три (два из которых вы забыли позвонить). По этой причине я рекомендую использовать pack, чтобы положить App внутри root. Это не обязательно для работы place, это просто делает ваш код более понятным.

Я также рекомендую не, несущий App несут ответственность за размещение себя в своем родителе. Вы можете, и снова, это не повлияет на то, как работает place, но это сделает ваш код более понятным, потому что у вас нет одной функции (App.__init__), ответственной за создание как главного окна, так и его дочерних элементов.

Учитывая это, следующее рабочее решение основано на вашем коде.Примечание. Существует также ошибка в том, как вы присваиваете значения списку, но исправление выходит за рамки этого вопроса.

#!/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() 
+0

Оцените свои указатели. Вернувшись на дорожку, вы узнали от вас систему мест. Мне придется расширить свой словарный запас по системам позиций. Также отмечена ваша рекомендация по поводу того, что «не было приложений, ответственных за размещение себя в своем родителе» * и * «у вас нет одной функции (App .__ init__), ответственной за создание как главного окна, так и его дочерних элементов». * , Я должен иметь приложение сетки (или пакета) в основном разделе моего кода. –