Соответствие OptionMenu и ширины кнопкиPython

Программы на Python
Ответить Пред. темаСлед. тема
Anonymous
 Соответствие OptionMenu и ширины кнопки

Сообщение Anonymous »

У меня есть OptionMenu и кнопка в соседних строках одного и того же столбца сетки. Я не хочу, чтобы сетка управляла размером этих виджетов, потому что она может сделать виджеты слишком широкими. Вместо этого я бы хотел, чтобы OptionMenu и Button имели фиксированный размер — максимальную ширину Button, ширину OptionMenu и ширину всех пунктов меню. Но у меня с этим проблемы.
Ширина и высота OptionMenu и Button изначально равны 1, поэтому сначала я должен связать , чтобы позволить геометрии сетки Менеджер сообщит мне желаемые размеры. Чтобы предотвратить бесконечный цикл, я немедленно отвязываю событие в обработчике.
Затем я могу вычислить ширину всех виджетов либо в пикселях, либо в текстовых единицах. Если я устанавливаю максимальную ширину в обработчике, я обнаруживаю, что «дескриптор» OptionMenu добавляет немного ширины, поэтому ширины кнопок и OptionMenu не совпадают.
Это вынуждает меня вкладывать друг в друга виджеты OptionMenu и Button внутри собственных фреймов (которые, в свою очередь, находятся внутри сетки). Затем я упаковываю OptionMenu и Button для расширения, но отключаю распространение пакетов в кадре. Идея состоит в том, чтобы установить ширину рамки и заставить вложенные виджеты расширяться до заполнения.
К сожалению, менеджер геометрии сетки сообщает моему обработчику, что OptionMenu и Button теперь запрашивают пространство размером 1x1 пиксель. , поскольку распространение пакетов отключено.
Далее я начал с нуля. Там, где будут OptionMenu и Button, я поместил фрейм с размахом строк, равным 2; Содержимое фрейма также управляется (вложенным) менеджером геометрии сетки. Рамка липкая="w"; он уменьшится, чтобы соответствовать своему содержимому. Содержимое липкое="ew"; они будут заполняться до одинакового размера.
Это работает хорошо, за исключением того, что там, где текст в столбцах одной строки будет иметь общую базовую линию, менеджер геометрии вложенной сетки не разделяет базовые линии с родительским менеджер геометрии сетки. Например, текст метки в первом столбце больше не имеет общей базовой линии с текстом OptionMenu во втором столбце. Вместо этого базовая линия текста метки в первом столбце теперь выровнена по нижней части OptionMenu.
Как это исправить?
Для Например, здесь я переместил метку «Ячейка (0,0)» во внутреннюю сетку. Это позволяет мне выровнять текст, но я теряю возможность центрировать вложенные столбцы внутри внешних столбцов.
#!/usr/bin/env python3

import tkinter as tk
import tkinter.font
import tkinter.scrolledtext

import PIL.Image
import PIL.ImageTk

class App:
options = ["short", "really really really really long"]
unselected = ""

def __init__(self, parent, **kwargs):
title = parent.winfo_toplevel().title()
font = tk.font.Font(family="Arial", size=12, weight="bold")

# Create a frame for the entire window
frame = tk.Frame(parent, **kwargs)
frame.pack(fill=tk.BOTH, expand=True)

# Set the title label from the window title
title_label = tk.Label(frame, text=title, font=font)
title_label.grid(row=0, columnspan=2, padx=10, pady=10)

# Load the logos
logo1_image = PIL.Image.new("RGB", (200,200), (0,255,0))
logo2_image = PIL.Image.new("RGB", (200,200), (255,0,0))

logo1_label = tk.Label(frame, borderwidth=0)
logo2_label = tk.Label(frame, borderwidth=0)

# TODO:
# I'm seeing slow-motion resize on large resizes. I believe that
# multiple resizes are being queued, but I can't cancel that queue. I
# need to make this callback schedule a cancellable callback that then
# resizes.
logo1_label.bind\
( ""
, lambda e, i=logo1_image, w=logo1_label: self.resizeimage(e, i, w)
)
logo2_label.bind\
( ""
, lambda e, i=logo2_image, w=logo2_label: self.resizeimage(e, i, w)
)

logo1_label.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
logo2_label.grid(row=1, column=1, padx=10, pady=10, sticky="nsew")

# Create a frame for interactive elements
#input_frame = tk.Frame(frame)
input_frame = tk.Frame(frame, bg="red")
input_frame.grid(row=2, columnspan=2)
input_frame.grid_columnconfigure(0, weight=1, uniform="column")
input_frame.grid_columnconfigure(1, weight=1, uniform="column")

# Label for cell (0,0)
c00_label = tk.Label\
(input_frame, font=font, text="cell (0,0)")
c00_label.grid(row=0, column=0, padx=10, pady=10, sticky="ew")

# Create the OptionMenu
options_var = tk.StringVar()
options_var.set(self.unselected)

options = tk.OptionMenu(input_frame, options_var, *self.options)
options.configure(font=font)
options["menu"].configure(font=font)
options.grid(row=0, column=1, padx=10, pady=10, sticky="ew")

# Create the Button
button = tk.Button\
( input_frame
, font=font, text="implement option"
)
button.grid(row=1, column=1, padx=10, pady=(0,0), sticky="ew")

# Create the labels for the 3rd row
c30_label = tk.Label\
(frame, font=font, text="cell (3,0)")
c30_label.grid(row=3, column=0, padx=10, pady=10, sticky="ew")

c31_label = tk.Label\
(frame, font=font, text="cell (3,1)")
c31_label.grid(row=3, column=1, padx=10, pady=10, sticky="ew")

text = tk.scrolledtext.ScrolledText\
(frame, state=tk.DISABLED, font=font)
text.grid(row=4, columnspan=2, padx=10, pady=10, sticky="nsew")

frame.grid_columnconfigure(0, weight=1, minsize=100)
frame.grid_columnconfigure(1, weight=1, minsize=100)
frame.grid_rowconfigure(0, weight=0)
frame.grid_rowconfigure(1, weight=1, minsize=100)
frame.grid_rowconfigure(2, weight=0)
frame.grid_rowconfigure(3, weight=0)
frame.grid_rowconfigure(4, weight=4)

@staticmethod
def resizeimage(event, image, widget):
image = image.copy()
image.thumbnail\
( (event.width, event.height)
, resample=PIL.Image.Resampling.BILINEAR
, reducing_gap=None
)
image = PIL.ImageTk.PhotoImage(image)
widget.config(image=image)
# Images are not referenced by tkinter
widget.image = image
#widget.unbind("")

def main():
root = tk.Tk()
root.title("Helpful Title")

app = App(root)

root.mainloop()

if __name__ == "__main__":
main()

Изменить. Вот наиболее рабочая версия варианта обратного вызова с описанными выше фреймами. Сначала вы упаковываете данные в кадр с включенным распространением. В обратном вызове явно установите высоту, равную текущей высоте, и ширину, равную максимальной ширине всех содержащихся виджетов, затем отключите распространение обратного вызова.
К сожалению, это Версия имеет две проблемы:
  • Измерение меток OptionMenu ошибочно, поэтому размер кнопок слишком мал, чтобы соответствовать действительно длинному пункту меню.
  • Изменение размера изображений вызывает бурю обратных вызовов; Я не знаю почему!
Как мне решить эти проблемы?
#!/usr/bin/env python3

import tkinter as tk
import tkinter.font
import tkinter.scrolledtext

import PIL.Image
import PIL.ImageTk

class App:
options = ["short", "really really really really really long"]
unselected = ""

def __init__(self, parent, **kwargs):
title = parent.winfo_toplevel().title()
font = tk.font.Font(family="Arial", size=12, weight="bold")

# Create a frame for the entire window
frame = tk.Frame(parent, **kwargs)
frame.pack(fill=tk.BOTH, expand=True)

# Set the title label from the window title
title_label = tk.Label(frame, text=title, font=font)
title_label.grid(row=0, columnspan=2, padx=10, pady=10)

# Load the logos
logo1_image = PIL.Image.new("RGB", (200,200), (0,255,0))
logo2_image = PIL.Image.new("RGB", (200,200), (255,0,0))

logo1_label = tk.Label(frame, bg="orange", borderwidth=0)
logo2_label = tk.Label(frame, bg="blue", borderwidth=0)

logo1_label.resize_count = 0
logo2_label.resize_count = 0
logo1_label.resize_id = None
logo2_label.resize_id = None
print("logo1_label: ", logo1_label)
print("logo2_label: ", logo2_label)

# TODO:
# I'm seeing slow-motion resize on large resizes. I believe that
# multiple resizes are being queued, but I can't cancel the queue. I
# need to make this callback schedule a cancellable callback that then
# resizes.
logo1_label.bind\
("", lambda e, i=logo1_image: self.onconfigure_image(e, i))
logo2_label.bind\
("", lambda e, i=logo2_image: self.onconfigure_image(e, i))

logo1_label.grid(row=1, column=0, padx=0, pady=0, sticky="nsew")
logo2_label.grid(row=1, column=1, padx=0, pady=0, sticky="nsew")

# Label for cell (2,0)
c00_label = tk.Label\
(frame, font=font, text="cell (2,0)")
c00_label.grid(row=2, column=0, padx=10, pady=10, sticky="ew")

# Create the OptionMenu
options_var = tk.StringVar()
options_var.set(self.unselected)

options_frame = tk.Frame(frame, bg="red")

options = tk.OptionMenu(options_frame, options_var, *self.options)
options.configure(font=font)
options["menu"].configure(font=font)

options.pack(fill=tk.BOTH, expand=True)
options_frame.grid(row=2, column=1, padx=10, pady=10)

# Create the Button
button_frame = tk.Frame(frame, bg="red")

button = tk.Button\
(button_frame, font=font, text="implement option")
button.pack(fill=tk.BOTH, expand=True)
button_frame.grid(row=3, column=1, padx=10, pady=(0,0))

button.bind("", self.onconfigure_button)

# not sure why I can't get the font from the widget, ie:
# font = tk.font.nametofont(self.options.cget("font"))
# *** _tkinter.TclError: named font font1 does not already exist
self.font = font
self.options_frame = options_frame
self.button_frame = button_frame
self.options = options
self.button = button

# Create the labels for the 4th row
c30_label = tk.Label\
(frame, font=font, text="cell (4,0)")
c30_label.grid(row=4, column=0, padx=10, pady=10, sticky="ew")

c31_label = tk.Label\
(frame, font=font, text="cell (4,1)")
c31_label.grid(row=4, column=1, padx=10, pady=10, sticky="ew")

text = tk.scrolledtext.ScrolledText\
(frame, state=tk.DISABLED, font=font)
text.grid(row=5, columnspan=2, padx=10, pady=10, sticky="nsew")

frame.grid_columnconfigure(0, weight=1, minsize=100, uniform="column")
frame.grid_columnconfigure(1, weight=1, minsize=100, uniform="column")
frame.grid_rowconfigure(0, weight=0)
frame.grid_rowconfigure(1, weight=1, minsize=100)
frame.grid_rowconfigure(2, weight=0)
frame.grid_rowconfigure(3, weight=0)
frame.grid_rowconfigure(4, weight=0)
frame.grid_rowconfigure(5, weight=4)

def onconfigure_button(self, event):
menu = self.options["menu"]
maxw = self.font.measure(self.unselected)
print(maxw)
for i in range(menu.index("end") + 1):
label = menu.entrycget(i, "label")
width = self.font.measure(label)
maxw = max(maxw, width)
print(label, width)
#print(label, label.winfo_width())
print("button width: ", self.button.winfo_width())
maxw = max(maxw, self.button.winfo_width())
print(maxw)
# Wrapping both widgets in frames allowed us to sidestep the padding
# added by the OptionMenu "handle". So while the widgets are now the
# same size, the "handle" width is not accounted for in the width
# calculations and may overlap with wide menu entries!
#
# I need a way to get the width of the menu "handle".
self.options_frame.config(width=maxw)
self.button_frame.config(width=maxw)
self.options_frame.config(height=self.options_frame.winfo_height())
self.button_frame.config(height=self.button_frame.winfo_height())
self.options_frame.pack_propagate(False)
self.button_frame.pack_propagate(False)
event.widget.unbind("")
return

# Question
# When I maximize the window, four events are generated, and I
# see the images scaled-up in a slow-motion animation. I try to cancel or
# skip older events, but each new waits till after the resize
# is complete... This suggests that the resize then affects the widget
# size, leading the callback to be called in a loop... but the showsize()
# callback shows that resizing the image does not change the widget size.
#
# Changing the resize to not preserve aspect ratio makes the problem much
# worse. Clearly the change in height is forcing the geometry manager to
# re-evaluate the layout. How do I prevent this?
def onconfigure_image(self, event, image):
print()
widget = event.widget
print("onconfigure for: ", widget)
#if widget.resize_i is not None:
# print("cancelling previous resize: ", widget.resize_id)
# widget.after_cancel(widget.resize_id)
#widget.resize_id = widget.after_idle(self.resize_image, event, image)
widget.resize_id = widget.after(1, self.resize_image, event, image)
widget.resize_count += 1
print("scheduling resize with id: ", widget.resize_id)

def showsize(self, widget):
print()
print("showsize for: ", widget)
print("widget size: ", widget.winfo_width(), widget.winfo_height())

def resize_image(self, event, image):
widget = event.widget
widget.resize_count -= 1
print()
if widget.resize_count > 0:
print(f"{widget.resize_count} events remaining, skipping resize")
return
print("widget size: ", widget.winfo_width(), widget.winfo_height())
print("resize_image for: ", widget)
print("resize id, resize count: ", (widget.resize_id, widget.resize_count))
widget.resize_id = None
#image = image.copy()
#image.thumbnail\
image = image.resize\
( (event.width, event.height)
, resample=PIL.Image.Resampling.BILINEAR
, reducing_gap=None
)
image = PIL.ImageTk.PhotoImage(image)
widget.config(image=image)
# Images are not referenced by tkinter
widget.image = image
#widget.unbind("")
widget.after(1, self.showsize, widget)

def main():
root = tk.Tk()
root.title("Helpful Title")

app = App(root)

root.mainloop()

if __name__ == "__main__":
main()


Подробнее здесь: https://stackoverflow.com/questions/790 ... tton-width
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение
  • Соответствие OptionMenu и ширины кнопки
    Anonymous » » в форуме Python
    0 Ответы
    18 Просмотры
    Последнее сообщение Anonymous
  • Соответствие OptionMenu и ширины кнопки
    Anonymous » » в форуме Python
    0 Ответы
    18 Просмотры
    Последнее сообщение Anonymous
  • Соответствие OptionMenu и ширины кнопки
    Anonymous » » в форуме Python
    0 Ответы
    20 Просмотры
    Последнее сообщение Anonymous
  • Соответствие ширины OptionMenu и Button (и изображений с изменяемым размером в расширяемых контейнерах)
    Anonymous » » в форуме Python
    0 Ответы
    20 Просмотры
    Последнее сообщение Anonymous
  • Сделать все кнопки одинаковой ширины, когда кнопки сложены для мобильных устройств и когда кнопки вложены на два уровня
    Anonymous » » в форуме Html
    0 Ответы
    64 Просмотры
    Последнее сообщение Anonymous

Вернуться в «Python»