Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Tkinter wrap widgets in frame if they reach the end of the screen

Is there something like pack to new line for Tk geometry manager? I am using pack to put widgets inside of a frame. On a high resolution screen, the widgets fit find side by side. However, if you put it into a smaller screen, the widgets will run out of room in the frame.

Basically, we go from:

Before

to:

after

You can see how it cuts off of my Processors entry field. The relevant code:

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)

A lot of the event binding and setting of the options may not be relevant, but I thought I should still include...just in case. Would the best option be to switch to a grid manager? The problem there would be this frame is packed inside of another one...and so on. Isn't it that if you start using the grid manager, you have to use it for everything because you can't mix packed and grid manager in the same frame?

like image 545
jwillis0720 Avatar asked Oct 03 '22 10:10

jwillis0720


1 Answers

AFAIK there is no inbuilt way of creating dynamic interfaces in tkinter. Your best bet is to get the screen resolution and then grid/pack appropriately.

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

So for example....

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)
like image 182
I_do_python Avatar answered Oct 07 '22 21:10

I_do_python