ython 3.12 Windows 10
I’m trying to create a property editor using tkinter. Here is the code:
import tkinter as tk
from tkinter import colorchooser
from tkinter.filedialog import asksaveasfile
from tkinter import ttk
# Third party
from tkscrolledframe import ScrolledFrame
import os
if os.name == 'nt':
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(1)
LABEL_WIDTH = 14
PADX = 5
PADY = 3
class StringWidget(tk.Frame):
''''''
def __init__(self, parent, label:str, default=""):
''''''
tk.Frame.__init__(self, parent)
self.label = tk.Label(self, text=label, anchor="e", width=LABEL_WIDTH)
self.entry = tk.Entry(self)
self.entry.insert(0, default)
self.label.pack(side="left", padx=PADX)
self.entry.pack(side="left", padx=PADX)
class FileWidget(tk.Frame):
''''''
def __init__(self, parent, label:str, default=""):
''''''
print(parent.winfo_width())
tk.Frame.__init__(self, parent, width=parent.winfo_width())
self.label = tk.Label(self, text=label, anchor="e", width=LABEL_WIDTH)
self.entry = tk.Entry(self)
self.entry.insert(0, default)
self.btn = tk.Button(self, text="Browse")
self.label.pack(side="left", padx=PADX)
self.entry.pack(side="left", padx=PADX)
self.btn.pack(side="left", padx=PADX)
class Point3DWidget(tk.Frame):
''''''
def __init__(self, parent, label:str, minv:int=0, maxv:int=10, default:int=0, incr:int=1, format="%.0f"):
''''''
tk.Frame.__init__(self, parent)
self.strv = tk.StringVar()
self.strv.set(default)
self.label = tk.Label(self, text=label, anchor='e', width=LABEL_WIDTH)
self.spinbox1 = ttk.Spinbox(self, from_=minv, to=maxv, increment=incr, format=format, textvariable=self.strv, wrap=True)
self.spinbox2 = ttk.Spinbox(self, from_=minv, to=maxv, increment=incr, format=format, textvariable=self.strv, wrap=True)
self.spinbox3 = ttk.Spinbox(self, from_=minv, to=maxv, increment=incr, format=format, textvariable=self.strv, wrap=True)
self.label.pack(side="left", padx=PADX)
self.spinbox1.pack(side="left", padx=PADX)
self.spinbox2.pack(side="left", padx=PADX)
self.spinbox3.pack(side="left", padx=PADX)
class AttributeEditor(tk.Frame):
''''''
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.label = tk.Label(self, text="Theme", font=('bold'))
self.sw = StringWidget(self, "String Widget", "Inigo")
self.fw = FileWidget(self, "File Widget", "Inigo")
self.pw = Point3DWidget(self, "Point 3D Widget", 0, 10, 6)
self.label.grid(row=0, column=0, sticky="w", padx=PADX, pady=PADY)
self.sw.grid(row=1, column=0, sticky="ew", pady=PADY)
self.fw.grid(row=2, column=0, sticky="ew", pady=PADY)
self.pw.grid(row=3, column=0, sticky="ew", pady=PADY)
separator = ttk.Separator(self, orient='horizontal')
separator.grid(row=9, column=0, sticky="ew", pady=PADX, padx=PADY)
if __name__ == "__main__":
root = tk.Tk()
root.title("Attribute Editor")
root.geometry("380x360")
# Create a ScrolledFrame widget
sf = ScrolledFrame(root)
sf.pack(side="top", expand=1, fill="both")
# Bind the arrow keys and scroll wheel
sf.bind_arrow_keys(root)
sf.bind_scroll_wheel(root)
# Create a frame within the ScrolledFrame
inner_frame = sf.display_widget(AttributeEditor)
root.mainloop()
When ever I resize the main window, the desired expending behavior I’m looking for is:
For reference
To achieve what you want, you need to:
fit_width=True
when calling sf.display_widget(...)
to expand the inner frame to the width of the canvas of ScrolledFrame
self.columnconfigure(0, weight)
inside AttributeEditor.__init__()
so that all child frames created inside it in column 0 will use all the horizontal available spacefill="x"
and expand=1
in self.entry.pack(...)
inside StringWidget.__init__()
to fulfill item #1fill="x"
and expand=1
in self.entry.pack(...)
inside FileWidget.__init__()
to fulfill item #2fill="x"
and expand=1
in those self.spinboxX.pack(...)
inside Point3DWidget.__init__()
to fulfill item #3Note also that in order to see the above result, you need to resize the window to a wider width.
Below is the required changes:
...
class StringWidget(tk.Frame):
def __init__(self, parent, label:str, default=""):
...
self.entry.pack(side="left", padx=PADX, fill="x", expand=1)
class FileWidget(tk.Frame):
def __init__(self, parent, label:str, default=""):
...
self.entry.pack(side="left", padx=PADX, fill="x", expand=1)
...
class Point3DWidget(tk.Frame):
def __init__(self, parent, label:str, minv:int=0, maxv:int=10, default:int=0, incr:int=1, format="%.0f"):
...
self.spinbox1.pack(side="left", padx=PADX, fill="x", expand=1)
self.spinbox2.pack(side="left", padx=PADX, fill="x", expand=1)
self.spinbox3.pack(side="left", padx=PADX, fill="x", expand=1)
class AttributeEditor(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.columnconfigure(0, weight=1)
...
if __name__ == "__main__":
...
root.geometry("800x360") # create wider window
...
inner_frame = sf.display_widget(AttributeEditor, fit_width=True)
root.mainloop()
The result: