2013-11-16 6 views
4

Есть что-то вроде пакета для новой строки для менеджера геометрии Tk? Я использую пакет для размещения виджетов внутри фрейма. На экране с высоким разрешением виджеты устанавливаются рядом друг с другом. Однако, если вы поместите его на меньший экран, виджеты будут работать вне комнаты в кадре.Python Tkinter обертывает виджеты в рамке, если они достигают конца экрана

В основном, мы идем:

Before

к:

after

Вы можете увидеть, как он отсекает мое поле процессоры ввода. Соответствующий код:

options_frame = ttk.LabelFrame(
     parent_frame, text="Blast Options") 
options_frame.pack(side=TOP, fill=X, expand=1, padx=5, pady=5) 
     self._set_up_blast_options(options_frame) 

def _set_up_blast_options(self, options_frame): 
    self.evalue = Tkinter.DoubleVar() 
    self.evalue.set(1) 
    self.word_size = Tkinter.IntVar() 
    self.word_size.set(4) 
    self.penalty_mismatch = Tkinter.DoubleVar() 
    self.penalty_mismatch.set(-4) 
    self.min_d_match = Tkinter.IntVar() 
    self.min_d_match.set(5) 
    self.proc_count = Tkinter.IntVar() 
    self.proc_count.set(cpu_count()) 

    # evalue 
    e_value_label = ttk.LabelFrame(
     options_frame, text="e-Value Threshold") 
    e_value_entry = ttk.Entry(e_value_label) 
    e_value_entry.insert(0, self.evalue.get()) 
    e_value_entry.bind('<Return>', self._validate_e_value) 
    e_value_entry.bind('<FocusOut>', self._validate_e_value) 
    e_value_label.pack(side=LEFT, expand=1, pady=5, padx=5, fill=X) 
    e_value_entry.pack(side=TOP, expand=1, pady=5, padx=5, fill=X) 

    # word size 
    word_size_label = ttk.LabelFrame(
     options_frame, text="Word Size") 
    word_size_entry = ttk.Entry(word_size_label) 
    word_size_entry.insert(0, self.word_size.get()) 
    word_size_entry.bind('<Return>', self._validate_word_value) 
    word_size_entry.bind('<FocusOut>', self._validate_word_value) 
    word_size_label.pack(side=LEFT, expand=1, pady=5, padx=5, fill=X) 
    word_size_entry.pack(side=TOP, expand=1, pady=5, padx=5, fill=X) 

    penalty_mismatch_label = ttk.LabelFrame(
     options_frame, text="Penalty Mismatch") 
    penalty_mismatch_entry = ttk.Entry(penalty_mismatch_label) 
    penalty_mismatch_entry.insert(0, self.penalty_mismatch.get()) 
    penalty_mismatch_entry.bind(
     '<Return>', self._validate_penalty_mismatch_value) 
    penalty_mismatch_entry.bind(
     '<FocusOut>', self._validate_penalty_mismatch_value) 
    penalty_mismatch_label.pack(side=LEFT, expand=1, pady=5, 
           padx=5, fill=X) 
    penalty_mismatch_entry.pack(side=TOP, expand=1, pady=5, 
           padx=5, fill=X) 
    # Min D Nucleotides 
    min_d_match_label = ttk.LabelFrame(
     options_frame, text="Minimal Number of D Nucleotides") 
    min_d_match_entry = ttk.Entry(min_d_match_label) 
    min_d_match_entry.insert(0, self.min_d_match.get()) 
    min_d_match_entry.bind(
     '<Return>', self._validate_min_match_value) 
    min_d_match_entry.bind(
     '<FocusOut>', self._validate_min_match_value) 
    min_d_match_label.pack(side=LEFT, expand=1, pady=5, padx=5, fill=X) 
    min_d_match_entry.pack(side=TOP, expand=1, pady=5, padx=5, fill=X) 

    # how many cpus to use 
    proc_count_label = ttk.LabelFrame(
     options_frame, text="Processors") 
    proc_count_entry = ttk.Entry(proc_count_label) 
    proc_count_entry.insert(0, self.proc_count.get()) 
    proc_count_entry.bind(
     '<Return>', self._validate_proc_count_value) 
    proc_count_entry.bind(
     '<FocusOut>', self._validate_proc_count_value) 
    proc_count_label.pack(side=LEFT, expand=1, pady=5, padx=5, fill=X) 
    proc_count_entry.pack(side=TOP, expand=1, pady=5, padx=5, fill=X) 

Много обязывающего события и настройки параметров не может быть уместным, но я думал, что я должен по-прежнему включать в себя ... на всякий случай. Будет ли лучший вариант переключиться на диспетчер сетки? Проблема в том, что этот кадр будет упакован внутри другого ... и так далее. Разве это не значит, что если вы начнете использовать диспетчер сетки, вы должны использовать его для всего, потому что вы не можете смешивать упакованный и сетчатый менеджер в том же фрейме?

+0

Вы можете смешивать пакет и сетку в одном приложении - на самом деле, это рекомендуется делать. Вы просто не можете смешивать сетку и пакет с виджетами с одним и тем же родителем. –

ответ

1

AFAIK нет встроенного способа создания динамических интерфейсов в tkinter. Лучше всего получить разрешение экрана, а затем сетку/пакет соответственно.

user32 = ctypes.windll.user32 
get_screensize = (user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)) 

Так, например ....

widget1 = tkinter.Entry(root,width=20,etc) 
widget2 = tkinter.Label(root,text='Hello',etc) 
## Now perform update_idletasks() which will force 
## Tkinter to calculate their size. 
widget1.update_idletasks() 
widget2.update_idletasks() 
## Now calculate whether they'll fit. 
if widget1.winfo_width() + widget2.winfo_width() > screensize[0]:## Not enough space 
    widget1.grid(row=0) 
    widget2.grid(row=1) 
else:## Both will fit on one line. 
    widget1.grid(row=0,column=0) 
    widget2.grid(row=0,column=1) 
1

В общем, я согласен с ответом I_do_python, но я хотел бы сделать одно небольшое изменение. Я бы поместил все виджеты в новый фрейм, и я бы использовал ширину рамки как тест лакмусовой бумаги о том, следует ли обертывать. И вы можете связать обработчик события для события "<Configure>", который затем проверяет, нужно ли обертывать содержимое фрейма при изменении размера окна приложения (и, следовательно, кадра).

Я написал следующую функцию, которая использует метод сетки для интеллектуального вставки виджетов в кадр. Это, безусловно, может быть улучшено, но это работает для меня. Обычно, если вы используете .grid(), чтобы заполнить родителя, вы можете потерять пустое пространство, когда виджеты заполнения меняются по ширине. Следующая функция обращается к этому, разбивая родителя на логические столбцы (divisions в коде) и позволяя виджетам охватывать несколько столбцов. Результатом является макет сетки с значительно уменьшенным белым пространством. Чем выше вы установили divisions, тем лучше результат, но значение по умолчанию должно работать в большинстве ситуаций.

def smart_grid(parent, *args, **kwargs): # *args are the widgets! 
    divisions = kwargs.pop('divisions', 100) 
    force_f  = kwargs.pop('force', False) 
    if 'sticky' not in kwargs: 
     kwargs.update(sticky='w') 
    try: 
     parent.win_width 
    except: 
     parent.win_width = -1 
    winfo_width = parent.winfo_width() 
    if 1 < winfo_width != parent.win_width or force_f: 
     parent.win_width = winfo_width 
     row = col = width = 0 
     argc = len(args) 
     for i in range(argc): 
      widget_width = args[i].winfo_width() 
      columns = max(1, int(widget_width * float(divisions)/winfo_width)) 
      width += widget_width 
      if width > winfo_width: 
       width = widget_width 
       row += 1 
       col = 0 
      args[i].grid(row=row, column=col, columnspan=columns, **kwargs) 
      col += columns 
     parent.update_idletasks() # update() # 
     return row + 1 

В конце концов я возьму несколько минут, чтобы переписать его с w.winfo_reqwidth() и будет обновлять свой пост.