I have a code, which creates an UI like this.
I want those page up/down buttons to be next to Page 1 label but I couldn't managed to do that. Only way I know with pack is the side option and it is not working well.
Second thing is, that scrollbar should be in that listbox. I know, I need to create a canvas then a frame in it. Then embed both listbox and scrollbar to them but I couldn't do that either.
This is my code.
class interface(tk.Frame):
def __init__(self,den):
self.pa_nu = 0 ##page number. Both used in labeling and result slicing
self.lbl1 = tk.Label(den, text="keyword")
self.lbl2 = tk.Label(den, text="Page %d" %(self.pa_nu+1))
self.ent1 = tk.Entry(den, takefocus=True)
self.btn1 = tk.Button(den, text="Search", command=self.button1)
self.btn2 = tk.Button(den, text="Page Up", command=self.page_up)
self.btn3 = tk.Button(den, text="Page Down", command=self.page_down)
scrollbar = tk.Scrollbar(den)
scrollbar.pack(side=RIGHT, fill=Y)
self.lst1 = tk.Listbox(den, selectmode="SINGLE", width="40", yscrollcommand=scrollbar.set)
self.lst1.bind("<Double-Button-1>", self.open_folder)
scrollbar.config(command=self.lst1.yview)
self.lbl1.pack(side="top")
self.ent1.pack()
self.btn1.pack(side="top")
self.btn2.pack(side="right")
self.btn3.pack(side="left")
self.lbl2.pack(side="bottom",padx=65)
self.lst1.pack(fill=BOTH)
def button1(self):
pass #some stuff here
def page_up(self):
pass #some stuff here
def page_down(self):
pass #some stuff here
def list_fill(self,i):
pass #some stuff here
def open_folder(self,event):
pass #some stuff here
There are three geometry managers in tkinter: place (absolute position), pack (good for line of widgets, or simple layout) and grid (complex layout).
Grid is worth looking for the layout you are working on. If you keep going with pack, the usual way to achieve complex layout is to use intermediate frames. For instance, in the following picture, all widgets in frame1 are packed vertically, and horizontally in frame2.
diagram with draw.io
Regarding the scrollbar, the usual way is again to use an intermediate frame (no need for a canvas). Here a snippet (copied from http://effbot.org/zone/tkinter-scrollbar-patterns.htm#listbox)
frame = Frame(root, bd=2, relief=SUNKEN)
scrollbar = Scrollbar(frame)
scrollbar.pack(side=RIGHT, fill=Y)
listbox = Listbox(frame, bd=0, yscrollcommand=scrollbar.set)
listbox.pack()
scrollbar.config(command=listbox.yview)
frame.pack() #or others...
Related
I'm making a text game. I used an entry widget for player input, a text widget for the game's output, and put them into frames. I set the root window's geometry and the frame sizes to fit into that geometry. However, the frame sizes are smaller than expected. Specifically, my story_text_frame is shorter than expected. I have done a tutorial, and am not sure what I am missing now.
import tkinter as tk
class Game(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args,**kwargs)
self.geometry('1280x720')
self.player_input_frame = tk.Frame(self, height=20, width=625)
self.player_input_field = tk.Entry(self.player_input_frame, background='black', foreground='white', relief='flat')
self.player_input_field.grid(row=0, column=0)
self.player_input_frame.grid(row=2, column=1)
self.story_text_frame = tk.Frame(self, height=670, width=625)
self.story_text_field = tk.Text(self.story_text_frame, background='grey', foreground='white')
self.story_text_field.grid(row=0, column=0)
self.story_text_frame.grid(row=1, column=1)
To have a widget size follow the size of a master widget or a window you must specify how this is to be done with columnconfigure() and rowconfigure(). Then you must expand the widget to fill the available cell space by instructing grid() to stick to the edges; sticky='nsew'. See my example:
import tkinter as tk
class Game(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args,**kwargs)
self.geometry('800x600')
self.columnconfigure(0, weight=1) # Specify how columns and rows will
self.rowconfigure(0, weight=1) # change when window size changes.
# The game's output Text() widget
self.story_text_field = tk.Text(self)
self.story_text_field.grid(row=0, column=0, sticky='nsew',
pady=10, padx=10) # Sticky expands the widget to fill the cell
# The player input Entry() widget
self.player_input_field = tk.Entry(self)
self.player_input_field.grid(row=1, column=0, pady=(0,10))
Game().mainloop()
I have removed the Frames to make the construction clearer. Also I removed the coloring of the widgets. I introduced some padding to make the result more pleasing to the eye and changed the window size to be easier to handle on my tiny screen.
For further information on grid() I can recommend effbot's The Tkinter Grid Geometry Manager. You can also read Mike - SMT's answer to Find position of another anchor than the anchor already used which elaborates on both grid() and pack().
I have a small python program to build a GUI. I'm trying to use a text widget to create an easily scrollable window that contains vertically stacked frames. A frame is created on button press and added to the bottom of the text widget. This works fine; however, I'm struggling to get these frames to stretch to fill the text box horizontally.
import Tkinter as tk
class NewEntry(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
#self.pack(fill="x", expand=True) #possible error source
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0,weight=1)
textField = tk.Entry(self)
textField.grid(row=1, column=0, padx=2, pady=1, sticky="ew")
addButton = tk.Button(self, text="Add", cursor="arrow")
addButton.grid(row=1, column=1, padx=10, pady=2, sticky="ew")
newLabel = tk.Label(self, text="Test", bg="#5522FF")
newLabel.grid(row=0, padx=2, pady=2, sticky="ew", columnspan=2)
newLabel.columnconfigure(0, weight=1)
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent)
self.parent=parent
self.grid()
self.columnconfigure(0, weight=1)
self.grid_rowconfigure(0,weight=1)
self.text = tk.Text(self, wrap="none", bg="#AA3333")
vsb = tk.Scrollbar(orient="vertical", command=self.text.yview)
self.text.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
self.text.pack(fill="both", expand=True)
b = tk.Button(self, text="Button #%s" % 1, command=self.OnButtonClick)
self.text.window_create("end", window=b)
self.text.insert("end", "\n")
def OnButtonClick(self):
self.text.configure(state="normal")
panel = NewEntry(self.text, bg="#FF1111")
self.text.window_create("end", window=panel)
self.text.insert("end", "\n")
self.text.configure(state="disabled")
if __name__=="__main__":
root = tk.Tk()
root.resizable(True, True)
appinstance=MainApplication(root)
appinstance.pack(fill="both", expand=True)
root.mainloop()
I've read many different posts talking about grid_columnconfigure, fill options, sticky options, etc, but I haven't been able to get it filling properly. I am wondering if the window_create() method of the Text widget creates some sort of size limitation? It seems as though my code in NewEntry class is properly filling the space allowed by the window_create method, but I don't know how to create a "panel" to fill the width of the text box.
I am also aware of the possibility of using a canvas instead of text box (I'm wanting to maintain dynamic size and scrollability, though). I read from several different posts that a text widget is easiest if you have a simple stack of widgets, though. I will accept any recommendation, though.
The root of the problem is that a frame typically wants to fit its contents. So, when you add the label, entry widget, and button to the frame, it will shrink to fit. Any size you give to the frame will be ignored.
There are several solutions to this problem. What many people do is turn geometry propagation off (eg: self.grid_propagate(False)) but that means you have to manage both the width and height when all you really want is to control the width.
Another solution is to put something inside the panel that can be configured with an explicit width, and which will then cause the containing frame to grow to fit. For example, you can add an invisible frame in row 0 that sits behind the other widgets in row 0. When you change the width of this frame it will cause the containing frame to grow:
class NewEntry(tk.Frame):
def __init__(...):
...
# create this _before_ creating the other widgets
# so it is lowest in the stacking order
self.sizer = tk.Frame(self, height=1)
self.sizer.grid(row=0, columnspan=2)
...
With that, you can force the panel to be any width you want by setting the width of the sizer, but tkinter will continue to compute the optimum height for the widget:
self.sizer.configure(width=200)
Now you just need to set up bindings so that whenever the text widget changes size, you call this function to resize each entry.
For example, you might want to save all of the panels in a list so that you can iterate over them later.
class MainApplication(...):
def __init__(...):
...
self.panels = []
...
def OnButtonClick(...):
...
panel = NewEntry(...)
self.panels.append(panel)
...
With that, you can set up a binding that triggers whenever the window resizes:
class MainApplication(...):
def __init__(...):
...
self.text.bind("<Configure>", self.OnConfigure)
...
def OnConfigure(self, event):
for panel in self.panels:
panel.sizer.configure(width=event.width)
I wouldn't do it precisely like that since it tightly couples the implementation of the panel to the main window, but it illustrates the general technique of explicitly controlling the width of an embedded window.
Other solutions involve putting the panels inside a containing frame, and make that frame be the only widget added to the text widget. You could also use a canvas instead of a text widget since it allows you to explicitly set the width and height of embedded windows.
I am building a GUI for a software and want to achieve this:
######################################
# | some title #
# menu upper |----------------------#
# | #
# | CANVAS #
# menu lower | #
# | #
#------------------------------------#
# statusbar #
######################################
Menu upper has some high level functionality, menu lower is changing in dependency of user input. Statusbar changes its contents often.
Unfortunately, Tkinter refuses to work.
Using the grid layout manager I were unable to create a stable design and adding content like labels and buttons to the menu on the left side:
self.root = tk.Tk()
self.root.resizable(width=0, height=0)
self.root.title("some application")
# menu left
self.menu_left = tk.Frame(self.root, width=150, bg="#ababab")
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="ns")
self.menu_left_upper = tk.Frame(self.menu_left, width=150, height=150, bg="red")
self.menu_left_upper.grid(row=0, column=0)
# this label breaks the design
#self.test = tk.Label(self.menu_left_upper, text="test")
#self.test.pack()
self.menu_left_lower = tk.Frame(self.menu_left, width=150, bg="blue")
self.menu_left_lower.grid(row=1, column=0)
# right area
self.some_title_frame = tk.Frame(self.root, bg="#dfdfdf")
self.some_title_frame.grid(row=0, column=1, sticky="we")
self.some_title = tk.Label(self.some_title_frame, text="some title", bg="#dfdfdf")
self.some_title.pack()
self.canvas_area = tk.Canvas(self.root, width=500, height=400, background="#ffffff")
self.canvas_area.grid(row=1, column=1)
self.root.mainloop()
This design worked without contents in the menu on the left side. Whenever I added something in self.menu_left_upper or self.menu_left_lower, like the test label, my design got broke: the menu frames disappeared.
Additionally, even with columnspan, I had to remove the statusbar, because when it was added the menus on the left disappeared, again.
Using pack layout manager I got this:
######################################
# | some title #
# |----------------------#
# menu upper | #
# | CANVAS #
# | #
# menu lower | #
# |----------------------#
# | statusbar #
######################################
Since I wanted the menu frame on the left to consume the full y-space I made it grow with pack(side="left", expand=True, fill="both"), but this setup always denies the statusbar to go for the full width.
Besides, the pure pack manager code looks "ugly". I think a design with a grid manager is "clearer". Therefore I thought a grid or a pack layout inside a grid layout would be nice?
Can anyone help? I am stuck in the GUI-hell :/
The key to doing layout is to be methodical, and to use the right tool
for the job. It also means that sometimes you need to be creative.
That means using grid when laying things out in a
grid, and using pack when laying things out top-to-bottom or
left-to-right.
The other key is to group your layout code together. It's much, much
easier to visualize and modify the layout when it's all in one block
of code.
In your case you seem to have three or four distinct areas, depending
on how you count. If you want to use grid, it will be easiest to
combine "menu upper" and "menu lower" into a frame, and treat that
whole frame as a table cell. It looks like you're already doing that,
which is good.
So, let's start with those main areas:
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="nsew")
self.some_title_frame.grid(row=0, column=1, sticky="ew")
self.canvas_area.grid(row=1, column=1, sticky="nsew")
# you don't have a status bar in the example code, but if you
# did, this is where you would put it
# self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")
Any time you use grid, you need to give at least one row and one
column a positive weight so that tkinter knows where to use any
unallocated space. Usually there is one widget that is the "main"
widget. In this case it's the canvas. You want to make sure that the
row and column for the canvas has a weight of 1 (one):
self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(1, weight=1)
note: using pack instead of grid would save you two lines of code, since pack doesn't require you to set weights the way grid does.
Next, we need to solve the problem of the menu areas. By default,
frames shrink to fit their contents, which is why adding the label
broke your layout. You weren't telling tkinter what to do with extra space, so the frames shrunk to fit, and extra space went unused.
Since you want "menu_upper" and "menu_lower" to
each share 50% of that area, pack is the simplest solution. You can
use grid, but it requires more lines of code to add the row and column weights.
self.menu_left_upper.pack(side="top", fill="both", expand=True)
self.menu_left_lower.pack(side="top", fill="both", expand=True)
Here is a functioning version, with statusbar. Notice how it behaves exactly as it should when you resize the window:
import Tkinter as tk
class Example():
def __init__(self):
self.root = tk.Tk()
self.root.title("some application")
# menu left
self.menu_left = tk.Frame(self.root, width=150, bg="#ababab")
self.menu_left_upper = tk.Frame(self.menu_left, width=150, height=150, bg="red")
self.menu_left_lower = tk.Frame(self.menu_left, width=150, bg="blue")
self.test = tk.Label(self.menu_left_upper, text="test")
self.test.pack()
self.menu_left_upper.pack(side="top", fill="both", expand=True)
self.menu_left_lower.pack(side="top", fill="both", expand=True)
# right area
self.some_title_frame = tk.Frame(self.root, bg="#dfdfdf")
self.some_title = tk.Label(self.some_title_frame, text="some title", bg="#dfdfdf")
self.some_title.pack()
self.canvas_area = tk.Canvas(self.root, width=500, height=400, background="#ffffff")
self.canvas_area.grid(row=1, column=1)
# status bar
self.status_frame = tk.Frame(self.root)
self.status = tk.Label(self.status_frame, text="this is the status bar")
self.status.pack(fill="both", expand=True)
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="nsew")
self.some_title_frame.grid(row=0, column=1, sticky="ew")
self.canvas_area.grid(row=1, column=1, sticky="nsew")
self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")
self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(1, weight=1)
self.root.mainloop()
Example()
On an unrelated note: I would strongly encourage you to not remove the ability for the user to resize the window. They know better than you what their requirements are. If you use grid and pack properly, the GUI will resize perfectly.
Adding the following code right before self.root.mainloop() achieves what you're looking for
self.some_status = tk.Label(self.root, text="status bar", bg="#dfdfdf")
self.some_status.grid(row=3, column=0, columnspan=2, sticky="we")
By putting in the line:
menu_left_upper.grid_propagate(False)
In between your menu_left_upper Frame and menu_left_upper.mainloop()
This works as:
By default, a container widget expands or collapses to be just big enough to hold its contents. Thus, when you call pack, it causes the frame to shrink. This feature is called geometry propagation.
For the vast majority of applications, this is the behavior you want. For those rare times when you want to explicitly set the size of a container you can turn this feature off. To turn it off, call either pack_propagate or grid_propagate on the container (depending on whether you're using grid or pack on that container), giving it a value of False.
See link to another question where this came from
To get your status bar just implement another frame and grid method:
status_bar_frame = Frame(root, bg="#dfdfdf")
status_bar_frame.grid(row=3, column=0, columnspan=2, sticky="we")
status_bar = Label(status_bar_frame, text="status bar", bg="#dfdfdf")
status_bar.pack()
Then your plan works.
Hope it helps :)
PS. Also why all the self attributes?
EDIT:
TO work you need to do:
menu_left_upper = Frame(menu_left, width=225, height=225, bg="red")
menu_left_upper.grid_propagate(False)
menu_left_upper.grid(row=0, column=0)
# this label breaks the design
test = Label(menu_left_upper, text="test", bg='red')
test.grid()
My result
I'm building an application that provides viewports for an internal data file. The files are somewhat complex, so I've created three sub-windows that handle different aspects of the file. The upper left provides an outline of the different sections, the upper right provides a series of text widgets containing errors found in the data file, and the lower provides a view of the datafile itself. To facilitate all of this, I wrote a small class that serves as a frame for each of these sections and can be populated with labels, textboxes, etc. (Code below.)
The problem I'm running into is that the text widgets in the upper right and lower sections do not expand with their containing frame. Based on various searches of effbot.org, Stackoverflow, and others, I think I have the settings correct, but obviously something is wrong. If I enlarge the main window, each section adapts as it should, but the text widgets don't expand left to right to fill the new subwindow dimensions.
Any tips are greatly appreciated.
Here's the class that provides functionality for the subwindows:
import Tkinter as tk
class ScrollingChildFrame(tk.Frame):
'''
A Tkinter class creating a scrollable window that can be used
in a docked multiple document interface form. The window created here
allows addition of widgets to create scrolling tables (spreadsheet like),
groups of text fields, etc.
'''
def __init__(self, root):
self.count = 0
tk.Frame.__init__(self)
self.root = root
self.canvas = tk.Canvas(self, height=self.winfo_height(), width=self.winfo_width() )
self.canvas.grid(row=0, column=0, sticky='nsew')
self.vsb = tk.Scrollbar(self, orient='vertical', command=self.canvas.yview)
self.vsb.grid(row=0,column=1,sticky='ns')
self.hsb = tk.Scrollbar(self, orient='horizontal', command=self.canvas.xview)
self.hsb.grid(row=1,column=0,sticky='ew')
self.intframe = tk.Frame(self.canvas)
self.intframe.config(height=self.winfo_height(), width=self.winfo_width())
self.canvas.configure(yscrollcommand=self.vsb.set, xscrollcommand=self.hsb.set)
self.canvas.create_window(0, 0, window=self.intframe, anchor='nw')
#set event bindings
self.bind('<Configure>', self.OnFrameConfigure)
self.intframe.bind('<Configure>', self.OnIntFrameConfigure)
def OnFrameConfigure(self, event=None):
'''
adjust canvas when main frame adjusts
'''
self.canvas.configure(width=event.width - self.vsb.winfo_width()-2,
height=event.height - self.hsb.winfo_height()-2)
def OnIntFrameConfigure(self, event=None):
'''
adjust the scrolling window when the internal frame is adjusted
'''
self.canvas.configure(scrollregion=self.canvas.bbox(tk.ALL))
Here's an example of how I'm using it with textboxes that don't expand:
import Tkinter as tk
from scrollingchildframe import *
class Vis_GUI:
'''
The main GUI class
'''
def __init__(self):
#tkinter stuff
self.root = tk.Tk()
self.root.geometry('500x500')
self.create_frames()
self.root.mainloop()
def create_frames(self):
'''
Build the GUI frames
'''
self.root.columnconfigure(0,weight=1)
self.root.columnconfigure(1,weight=3)
self.root.rowconfigure(0,weight=1)
self.root.rowconfigure(1,weight=3)
#data blocks
self.block_frame = ScrollingChildFrame(self.root)
self.block_frame.config(height=200, width=200)
##error list
self.error_frame = ScrollingChildFrame(self.root)
self.error_frame.config(height=200, width=300)
##data
self.data_frame = ScrollingChildFrame(self.root)
self.data_frame.config(height=300, width=500)
##populate with empty cells
self.PopulateEmpty()
##place them on the grid
self.block_frame.grid(row=0, column=0, padx=2, pady=2, sticky='nsew')
self.error_frame.grid(row=0,column=1, padx=2, pady=2, sticky='nsew')
self.data_frame.grid(row=1, column=0, columnspan=2, padx=2,pady=2, sticky='nsew')
def PopulateEmpty(self):
'''
Populate the frames with empty contents so it doesn't look quite so empty.
'''
z = tk.Text(self.data_frame.intframe)
z.insert(tk.INSERT, 'blah\nblah\nblah')
height = float(z.index(tk.END))
z.config( height=height, state=tk.DISABLED, wrap=tk.NONE)
z.pack(anchor='nw', expand=1, fill=tk.X)
z = tk.Text(self.error_frame.intframe, height=1)
z.pack(anchor='w', expand = 1, fill=tk.X)
z = tk.Label(self.block_frame.intframe, text = 'No file open')
z.pack(anchor='w')
if (__name__=="__main__"):
wv = Vis_GUI()
The Frame also has to have expand and fill options set (and you will have to check on what ScrollingChildFrame does-and this is not a complaint about incomplete code, just pointing out the next step). Using just pack() for the Frame in the following code will not allow it to expand. You can uncomment it and comment the other pack if you want to see the difference.
try:
import Tkinter as tk ## Python 2.x
except ImportError:
import tkinter as tk ## Python 3.x
top=tk.Tk()
## use separate frame instead of top
fr=tk.Frame(top)
##fr.pack() ## does not expand
fr.pack(anchor='nw', expand=1, fill=tk.X)
z = tk.Text(fr)
insert_text="%s" % ("blah"*25) + 'blah\nblah\nblah'
z.insert(tk.INSERT, insert_text)
height = float(z.index(tk.END))
z.config( height=height, state=tk.DISABLED, wrap=tk.NONE)
z.pack(anchor='nw', expand=1, fill=tk.X)
top.mainloop()
I'm trying to resize a window in my GUI but one of my frames is getting left out and I'm not sure why. The window resizes fine horizontally, but when I try to resize vertically the frame with the button disappears. This is my first GUI so I'm sure there is something I'm missing...
from Tkinter import *
from ttk import *
class GUI(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.root = root
lbFrame = Frame(self.root)
nbFrame = Frame(self.root)
self.note = Notebook(nbFrame)
self.note.pack(fill=BOTH, expand=YES)
lbFrame.pack(side=LEFT, fill=BOTH, expand=YES)
nbFrame.pack(side=RIGHT, fill=BOTH, expand=YES)
self.make_file_viewer()
# Label
lblabel = Label(lbFrame, text='Files', background='#E8E8E8')
lblabel.pack(side=TOP, expand=YES, padx=10, pady=10)
# Listbox
self.lb = Listbox(lbFrame, height=49, borderwidth=0, font=('Purisa', 11), selectmode=EXTENDED)
self.lb.pack(side=BOTTOM, expand=YES, padx=10, pady=10)
def make_file_viewer(self):
fvwr = Frame(self.note)
dataFrm = Frame(fvwr)
btnFrm = Frame(fvwr)
dataFrm.pack(side=TOP, fill=BOTH, expand=YES)
btnFrm.pack(side=BOTTOM, fill=BOTH, expand=YES)
fvwr.config(borderwidth=2)
self.note.add(fvwr, text='File View')
# Label
self.lbl_fvwr_search = Label(dataFrm, text='Search Hits\t0', justify=LEFT)
self.lbl_fvwr_search.pack(side=TOP, anchor=W, expand=YES)
# Scrollbar
scrollbar_fvwr = Scrollbar(dataFrm)
scrollbar_fvwr.pack(side=RIGHT, fill=Y, expand=YES)
# Textbox
self.outputPanel_fvwr_text = Text(dataFrm, wrap='word', height=40, width=115, yscrollcommand=scrollbar_fvwr.set)
self.outputPanel_fvwr_text.pack(side=LEFT, fill=BOTH, expand=YES)
scrollbar_fvwr.config(command=self.outputPanel_fvwr_text.yview)
# Start button
viewBtn = Button(btnFrm, text='Start', width=8)
viewBtn.pack(anchor=W, expand=YES)
if __name__ == '__main__':
root = Tk()
app = GUI(root)
root.mainloop()
The absolute best thing you can do is to start over, and do your layout step-by-step. Start by creating the main areas, and make sure they resize properly. In your case, create the left and right sides. Again, get those two sides resizing properly with respect to each other.
Once you are done, focus on one section. Since you know the main section resizes properly, you only need to focus on the elements within that particular side. Again, break it down into pieces, and get those pieces working before tackling any widgets inside the main pieces.
When you do your layout this way, it's much easier to get the whole GUI working right, because you aren't trying to juggle the behavior of a half dozen widgets at once.
In your specific case, the root of the problem is that you have expand=YES for just about everything. As a general rule of thumb, you only want to set that to YES for one widget in an given parent window. For example, in your main window you want the right to expand but not the left (I'm guessing), and in the right window you want the text widget to expand but not the other widgets.
Set expand=NO for scrollbar_fvwr, self.lbl_fvwr_search, and btnFrm to get the right side to resize properly. For the left side, add fill=BOTH for self.lb, and expand=NONE for lblabel.