Python Tkinter оборачивает виджеты во фрейм, если они достигают конца экрана

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

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

До

to:

после

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

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)

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


person jwillis0720    schedule 16.11.2013    source источник
comment
Вы можете смешивать пакет и сетку в одном приложении - на самом деле, это рекомендуется делать. Вы просто не можете смешивать сетку и упаковку с виджетами, имеющими одного и того же родителя.   -  person Bryan Oakley    schedule 17.12.2013


Ответы (2)


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)
person I_do_python    schedule 29.06.2015

В целом я согласен с ответом 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(), и обновлю свой пост.

person Gary02127    schedule 12.02.2016