Python Tkinter помещает виджеты в рамку, если они доходят до конца экрана

StackOverflow https://stackoverflow.com//questions/20012860

Вопрос

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

По сути, мы исходим из:

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)

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

Это было полезно?

Решение

Афайк Нет встроенного способа создания динамических интерфейсов в 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)
.

Другие советы

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

Я написал приведенную ниже функцию, которая использует метод grid для интеллектуальной вставки виджетов во фрейм.Это, конечно, можно улучшить, но у меня это работает.Обычно, если вы используете .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() и обновлю свой пост.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top