tkinter: scrollbar autohide without window resize - python

Using the folowing sample code I wrote I am having issues with some behavior.
I want to add/remove the scrollbar as needed. But when I do it shifts all other elements in the window as the window resizes. This is just a sample to demonstrate the issue, you will see the window resize when the scrollbar is added and removed. In the real application there are more widgets on the window.
Am I trying to do this the right way or if not how can I resolve the issue? I also plan to have a second widget with scrollbars as well in another separate frame.
from tkinter import *
from tkinter import ttk
class TopFrame(ttk.Frame):
def __init__(self, parent, col=0, row=0):
ttk.Frame.__init__(self, parent)
self.innerframe = ttk.Frame(parent)
self.list_scroll = ttk.Scrollbar(self.innerframe)
self.list_scroll.grid(column=1, row=0, sticky=NS)
self.list_scroll.grid_remove()
self.list = Listbox(self.innerframe, width=64, height=8,
yscrollcommand=self.list_scroll.set)
self.list_scroll.config(command=self.list.yview)
self.list.grid(column=0, row=0, sticky=NSEW)
self.innerframe.grid(column=col, row=row)
self.addbtn = ttk.Button(parent, text='add item',
command=self.additem)
self.addbtn.grid(column=col, row=row+1, padx=10, pady=2)
self.delbtn = ttk.Button(parent, text='del item',
command=self.delitem)
self.delbtn.grid(column=col, row=row+2, padx=10, pady=2)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
def additem(self):
count = str(len(self.list.get(0, END)))
self.list.insert(END, 'demo' + count)
if len(self.list.get(0, END)) > 8:
self.list_scroll.grid()
def delitem(self):
self.list.delete(END)
if len(self.list.get(0, END)) <= 8:
self.list_scroll.grid_remove()
class MasterFrame(Tk):
def __init__(self):
Tk.__init__(self)
topframe = TopFrame(self)
if __name__ == '__main__':
MasterFrame().mainloop()

Once the window has been displayed for the first time you can get the window size, and then use that to call the geometry method on the root window. When you set the size of the window with the geometry command it will stop resizing based on changes to its internal widgets.
The simplest thing is to write a function to do that, and schedule it to run with after_idle, which should fire after the window is first displayed.

Related

Label text isn't appearing in tkinter Frame

I've been researching this question, but none of the solutions I've found seem to work. I'm trying to get a Label (self.status_bar in the code below) to appear in my frame, but any edits (i.e. using update() method or resizing the frame/text widget/window) I've made have gotten me nowhere.
from tkinter import *
from tkinter import filedialog
from tkinter import font
#Build frame with features to put into parent window
class TextEditor:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
#Create Scrollbar
self.text_scroll = tk.Scrollbar(self.frame)
self.text_scroll.pack(side=RIGHT, fill=Y)
#Create text box
self.text = tk.Text(self.frame, width=155, height=55, font=('Helvetica', 12), selectbackground="yellow",
selectforeground = "black", undo=True, yscrollcommand=self.text_scroll.set)
self.text.pack()
#Configure scrollbar
self.text_scroll.config(command=self.text.yview)
#Create menu
self.menu = tk.Menu(self.master)
self.master.config(menu=self.menu)
#Add file menu
self.file_menu = tk.Menu(self.menu, tearoff=False)
self.menu.add_cascade(label="File", menu=self.file_menu)
self.file_menu.add_command(label="Open")
self.file_menu.add_command(label="Save")
self.file_menu.add_command(label="New")
self.file_menu.add_separator()
self.file_menu.add_command(label="Exit", command=self.master.destroy)
#Add edit menu
self.edit_menu = tk.Menu(self.menu, tearoff=False)
self.menu.add_cascade(label="Edit", menu=self.edit_menu)
self.edit_menu.add_command(label="Cut")
self.edit_menu.add_command(label="Copy")
self.edit_menu.add_command(label="Undo")
self.edit_menu.add_command(label="Redo")
#Add status bar to bottom of app
self.status_bar = tk.Label(self.frame, text="Ready", anchor=E)
self.status_bar.pack(fill=X, side=BOTTOM, ipady=5)
#Pack frame into window
self.frame.pack()
#Instantiates the text editor app
def main():
root = tk.Tk()
app = TextEditor(root)
root.geometry("1220x660")
root.title("Text Editor")
root.mainloop()
if __name__ == '__main__':
main()
You are forcing the window to a size that is too small to fit all of the widgets. When you do that while using pack, pack will start to shrink widgets in order to make them fit, starting with the last widget that was packed. In this case that's the status bar. So, pack starts removing pixels from self.status_bar until there's enough room for the other widgets. Eventually, it has to completely remove the status bar, and then start shrinking the text widget.
The first step is to create the status bar first, so that the text widget is higher in the stacking order (ie: pack will try to shrink it before shrinking other widgets).
The second thing you should do is use the appropriate options to get the TextEditor window to fill the frame, and get the frame to fill the window. For example:
self.text.pack(fill="both", expand=True)
self.frame.pack(fill="both", expand=True)
I suggest, with the more complicated layout you have, that you use the grid method instead of pack. Here is the code with the widgets gridded instead of packed:
import tkinter as tk
from tkinter import filedialog
from tkinter import font
from tkinter.constants import *
#Build frame with features to put into parent window
class TextEditor:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
#Create Scrollbar
self.text_scroll = tk.Scrollbar(self.frame)
self.text_scroll.grid(row=0, column=1, sticky=E+NS) ### EDITED THIS LINE
#Create text box
self.text = tk.Text(self.frame, font=('Helvetica', 12), selectbackground="yellow", ### EDITED THIS LINE
selectforeground = "black", undo=True, yscrollcommand=self.text_scroll.set)
self.text.grid(row=0, column=0, sticky=NSEW) ### EDITED THIS LINE
#Configure scrollbar
self.text_scroll.config(command=self.text.yview)
#Create menu
self.menu = tk.Menu(self.master)
self.master.config(menu=self.menu)
#Add file menu
self.file_menu = tk.Menu(self.menu, tearoff=False)
self.menu.add_cascade(label="File", menu=self.file_menu)
self.file_menu.add_command(label="Open")
self.file_menu.add_command(label="Save")
self.file_menu.add_command(label="New")
self.file_menu.add_separator()
self.file_menu.add_command(label="Exit", command=self.master.destroy)
#Add edit menu
self.edit_menu = tk.Menu(self.menu, tearoff=False)
self.menu.add_cascade(label="Edit", menu=self.edit_menu)
self.edit_menu.add_command(label="Cut")
self.edit_menu.add_command(label="Copy")
self.edit_menu.add_command(label="Undo")
self.edit_menu.add_command(label="Redo")
#Add status bar to bottom of app
self.status_bar = tk.Label(self.frame, text="Ready", anchor=E)
self.status_bar.grid(row=1, column=0, sticky=S+EW) ### EDITED THIS LINE
# Configure the rows and columns so that they expand properly ### ADDED THESE LINES
self.frame.rowconfigure(0, weight=1) ### ADDED THESE LINES
self.frame.columnconfigure(0, weight=1) ### ADDED THESE LINES
#Pack frame into window
self.frame.pack(expand=YES, fill=BOTH) ### EDITED THIS LINE
#Instantiates the text editor app
def main():
root = tk.Tk()
app = TextEditor(root)
root.geometry("1220x660")
root.title("Text Editor")
root.mainloop()
if __name__ == '__main__':
main()
Notice how I also changed the line where the frame is packed into the window. The only thing keeping the frame filling the window before was the size of the text widget.
With these changes, the widgets expand properly, so I also removed the width and height parameters from the creation of self.text.

Tkinter window not updating after changing the window.columnconfigure()

I just started using python and I am trying to make a simple gui that consists of 3 frames. One on the left, one on the right and one in the middle. Later I'd like to add buttons and stuff to those frames but for now that is all. I want the left frame to disappear or appear again if I press the escape key. To do this I have written the following code:
from tkinter import Tk, Button, Label, Frame
class Main:
def __init__(self):
self.root = Tk()
self.init_gui()
def init_gui(self):
self.root.title("Gui Testing")
self.root.minsize(900,600)
self.root.bind("<Escape>", self.toggle_left_menu)
self.root.grid_columnconfigure(0, minsize=200)
self.root.grid_columnconfigure(1, weight=1)
self.root.grid_columnconfigure(2, minsize=250)
self.root.grid_rowconfigure(0, weight=1)
# main 3 panels
self.left_menu_active = True
self.left_menu = Frame(self.root, bg="#333")
self.left_menu.grid(row=0, column=0, sticky="nsew")
self.center = Frame(self.root, bg="white")
self.center.grid(row=0, column=1, sticky="nsew")
self.right_menu = Frame(self.root, bg="#888")
self.right_menu.grid(row=0, column=2, sticky="nsew")
self.toggle_left_menu()
def toggle_left_menu(self, event=None):
if self.left_menu_active == True:
self.left_menu_active = False
self.root.grid_columnconfigure(0, minsize=0)
self.left_menu.grid_forget()
else:
self.left_menu_active = True
self.root.grid_columnconfigure(0, minsize=200)
self.left_menu.grid(row=0, column=0, sticky="nsew")
def start(self):
self.root.mainloop()
Main().start()
The problem is that when I press escape, nothing happens. However, when I then move the window by clicking on it and dragging it, it updates all of a sudden and it shows the window the way I want it. So the code seems to be working but the window isn't updating for some reason.
I don't know what I can do about that. I found out that it does update the grid positions of the left and the center frame, but the grid_configure() doesn't seem to update without moving the window.
Is there a way to update the frame or to achieve the frame toggling in some other way?
Edit:
The problem has been solved by adding a button to each frame. Now the frames are not empty anymore it seems works. I also edited the toggle_left_menu() function a bit. This is what I changed:
Added Buttons:
self.test_button1 = Button(self.left_menu, text="left", padx=10, pady=5)
self.test_button1.grid(row=0, column=0)
self.test_button2 = Button(self.center, text="center", padx=10, pady=5)
self.test_button2.grid(row=0, column=0)
self.test_button3 = Button(self.right_menu, text="right", padx=10, pady=5)
self.test_button3.grid(row=0, column=0)
Edited toggle_left_menu():
def toggle_left_menu(self, event=None):
if self.left_menu.winfo_viewable():
self.root.grid_columnconfigure(0, minsize=0)
self.left_menu.grid_remove()
else:
self.root.grid_columnconfigure(0, minsize=200)
self.left_menu.grid()
This worked for me, thanks!
Extending Bryan Oakley's example ~ you have to toggle the minsize, as well. As an aside, I classed out all of your gui and made Main the root. All that self.root.this and self.root.that is unnecessary this way. Also, you would have had to do this anyway, unless you intended to dump your entire gui contents into your init_gui method. If your app is large that would be a nightmare to keep track of. As an added bonus, I made the whole toggle_menu method dynamic so it can toggle either menu. You can change the key-bindings, to whatever. I used Escape then l for left_menu and Escape then r for right_menu.
from tkinter import Tk, Button, Label, Frame
class LeftMenu(Frame):
#property
def minsize(self):
return 200
def __init__(self, master, row=0, column=0, **kwargs):
Frame.__init__(self, master, **kwargs)
self.grid(row=row, column=column, sticky='nswe')
class RightMenu(Frame):
#property
def minsize(self):
return 250
def __init__(self, master, row=0, column=0, **kwargs):
Frame.__init__(self, master, **kwargs)
self.grid(row=row, column=column, sticky='nswe')
class Center(Frame):
def __init__(self, master, row=0, column=0, **kwargs):
Frame.__init__(self, master, **kwargs)
self.grid(row=row, column=column, sticky='nswe')
class Main(Tk):
def __init__(self):
Tk.__init__(self)
self.bind("<Escape><l>", self.toggle_menu)
self.bind("<Escape><r>", self.toggle_menu)
self.grid_columnconfigure(0, minsize=200)
self.grid_columnconfigure(1, weight=1)
self.grid_columnconfigure(2, minsize=250)
self.grid_rowconfigure(0, weight=1)
# main 3 panels
self.left_menu = LeftMenu(self, 0, 0, bg="#333")
self.center = Center(self, 0, 1, bg="white")
self.right_menu = RightMenu(self, 0, 2, bg="#888")
self.toggle_menu(menu=self.left_menu)
def toggle_menu(self, event=None, menu=None):
if event and event.char in 'lr':
menu = self.left_menu if event.char == 'l' else self.right_menu
if menu:
if menu.winfo_viewable():
self.grid_columnconfigure(menu.grid_info()['column'], minsize=0)
menu.grid_remove()
else:
menu.grid()
self.grid_columnconfigure(menu.grid_info()['column'], minsize=menu.minsize)
self.after_idle(self.event_generate, '<Configure>')
if __name__ == "__main__":
root = Main()
root.title("Gui Testing")
root.minsize(900,600)
root.mainloop()
Part of the problem is that minsize only affects the minimum size. If the left frame is visible and is more than zero pixels wide, setting the minsize to zero isn't going to make the frame smaller. So, one step is to remove the minsize=200 option for column 0.
Since you are using grid, the best way to hide or show a frame is to use grid_remove to remove the widget, and then grid to restore it. grid_remove will remove the widget from the window but remember all of its settings. When you subsequently call .grid(), all of the previous settings will be used.
You can also just check if the window is visible or not without having to manage a boolean flag since your function is a toggle. That simplifies the code a bit.
Also, I think there's a bug on some versions of tk (the library upon which tkinter is built) that prevents the window from refreshing in this specific type of situation. What works for me is to synthetically generate a <Configure> event on the root window.
Rolling all of that together, this version of your toggle function works for me on OSX without any other modifications to your code.
def toggle_left_menu(self, event=None):
if self.left_menu.winfo_viewable():
self.left_menu.grid_remove()
self.root.after_idle(self.root.event_generate, '<Configure>')
else:
self.left_menu.grid()
self.root.after_idle(self.root.event_generate, '<Configure>')

Tkinter Dynamic scrollbar for a dynamic GUI not updating with GUI

This is related to a previous question:
Tkinter dynamically create widgets from button
At the time that I asked the previous question, I believed that it would be easy to add a scrollable frame around the dynamic GUI. Instead, I have had a single problem with the scrollbar not detecting the new frames and entry boxes after the button is pressed. How do I solve this without editing the ScrollFrame class much?
I know that the Scrollbarframe works with other widgets it is just that the dynamic component is causing issues. When I shrink the vertical size of the window past the original location of the createWidgets button, the scrollbar appears, but the scrollbar is not present for the rest of the dynamically created widgets. Does the canvas not detect that the vertical size of the frame increases with a button press?
Note: I am aware that wildcard imports are awful. I'm just using one for the example
from tkinter import *
class AutoScrollbar(Scrollbar):
# A scrollbar that hides itself if it's not needed.
# Only works if you use the grid geometry manager!
def set(self, lo, hi):
if float(lo) <= 0.0 and float(hi) >= 1.0:
# grid_remove is currently missing from Tkinter!
self.tk.call("grid", "remove", self)
else:
self.grid()
Scrollbar.set(self, lo, hi)
def pack(self, **kw):
raise TclError("cannot use pack with this widget")
def place(self, **kw):
raise TclError("cannot use place with this widget")
class ScrollFrame:
def __init__(self, master):
self.vscrollbar = AutoScrollbar(master)
self.vscrollbar.grid(row=0, column=1, sticky=N+S)
self.hscrollbar = AutoScrollbar(master, orient=HORIZONTAL)
self.hscrollbar.grid(row=1, column=0, sticky=E+W)
self.canvas = Canvas(master, yscrollcommand=self.vscrollbar.set,
xscrollcommand=self.hscrollbar.set)
self.canvas.grid(row=0, column=0, sticky=N+S+E+W)
self.vscrollbar.config(command=self.canvas.yview)
self.hscrollbar.config(command=self.canvas.xview)
# make the canvas expandable
master.grid_rowconfigure(0, weight=1)
master.grid_columnconfigure(0, weight=1)
# create frame inside canvas
self.frame = Frame(self.canvas)
self.frame.rowconfigure(1, weight=1)
self.frame.columnconfigure(1, weight=1)
def update(self):
self.canvas.create_window(0, 0, anchor=NW, window=self.frame)
self.frame.update_idletasks()
self.canvas.config(scrollregion=self.canvas.bbox("all"))
if self.frame.winfo_reqwidth() != self.canvas.winfo_width():
# update the canvas's width to fit the inner frame
self.canvas.config(width = self.frame.winfo_reqwidth())
if self.frame.winfo_reqheight() != self.canvas.winfo_height():
# update the canvas's width to fit the inner frame
self.canvas.config(height = self.frame.winfo_reqheight())
frames = []
widgets = []
def createwidgets():
global widgetNames
global frameNames
frame = Frame(o.frame, borderwidth=2, relief="groove")
frames.append(frame)
frame.pack(side="top", fill="x")
widget = Entry(frame)
widgets.append(widget)
widget.pack(side="left")
root = Tk()
o = ScrollFrame(root)
label = Label(o.frame, text = "test")
label1 = Label(o.frame, text = "test")
label2 = Label(o.frame, text = "test")
label3 = Label(o.frame, text = "test")
label.pack()
label1.pack()
label2.pack()
label3.pack()
createWidgetButton = Button(o.frame, text="createWidgets",
command=createwidgets)
createWidgetButton.pack(side="bottom", fill="x")
o.update()
root.mainloop()
This is what the window would look like if it was fully expanded
If I were to shrink the window, it should immediately create a vertical scrollbar because that would cover a widget. However, the scrollbar acts like the program was still in its initial state.
Incorrect Scrollbar(at the moment that the scrollbar appears)
You need to make sure that you update the canvas scrollregion whenever you add widgets to the inner frame. The most common solution is to bind to the frame's <Configure> event, which will fire whenever the frame changes size.
In ScrollFrame.__init__ add the following line after you create the frame:
self.frame.bind("<Configure>", self.reset_scrollregion)
Then, add this function to ScrollFrame:
def reset_scrollregion(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all")

Dynamic Button with ScrollBar in tkinter - Python

I had an requirement for creating dynamic buttons in tkinter window,But i tried Scroll bar option which is not helping me to scroll the buttons in the tkinter window,Is any other option to scroll the Dynamic buttons.
Code:
root = tkinter.Tk()
root.title("Links-Shortcut")
root.configure(background="gray99")
sw= tkinter.Scrollbar(root)
sw.pack(side=RIGHT, fill=Y)
os.chdir("C:\Bo_Link")
with open('Bo_ol_links.csv', 'r', newline='') as fo:
lis=[line.strip('\r\n').split(',') for line in fo] # create a list of lists
lis=sorted(lis)
#print (lis)
for i,x in enumerate(lis):
btn = tkinter.Button(root,height=1, width=20,relief=tkinter.FLAT,bg="gray99",fg="purple3",font="Dosis",text=lis[i][0],command=lambda i=i,x=x: openlink(i))
btn.pack(padx=10,pady=5,side=tkinter.TOP)
def openlink(i):
os.startfile(lis[i][1])
root.mainloop()
Thanks.
This code packs buttons into a scrollable Frame that I stole from found at the Tkinter Unpythonic Wiki. I'm running it on Python 2, so I use Tkinter as the module name in the import statement, for Python 3 change that statement to use tkinter.
import Tkinter as tk
class VerticalScrolledFrame(tk.Frame):
"""A pure Tkinter scrollable frame that actually works!
* Use the 'interior' attribute to place widgets inside the scrollable frame
* Construct and pack/place/grid normally
* This frame only allows vertical scrolling
"""
def __init__(self, parent, *args, **kw):
tk.Frame.__init__(self, parent, *args, **kw)
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL)
vscrollbar.pack(fill=tk.Y, side=tk.RIGHT, expand=tk.FALSE)
canvas = tk.Canvas(self, bd=0, highlightthickness=0,
yscrollcommand=vscrollbar.set)
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.TRUE)
vscrollbar.config(command=canvas.yview)
# reset the view
canvas.xview_moveto(0)
canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = tk.Frame(canvas)
interior_id = canvas.create_window(0, 0, window=interior,
anchor=tk.NW)
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the canvas's width to fit the inner frame
canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the inner frame's width to fill the canvas
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
canvas.bind('<Configure>', _configure_canvas)
root = tk.Tk()
root.title("Scrollable Frame Demo")
root.configure(background="gray99")
scframe = VerticalScrolledFrame(root)
scframe.pack()
lis = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
for i, x in enumerate(lis):
btn = tk.Button(scframe.interior, height=1, width=20, relief=tk.FLAT,
bg="gray99", fg="purple3",
font="Dosis", text='Button ' + lis[i],
command=lambda i=i,x=x: openlink(i))
btn.pack(padx=10, pady=5, side=tk.TOP)
def openlink(i):
print lis[i]
root.mainloop()
Believe it or not, the simplest solution for a vertical stack of buttons might be to add the buttons to a text widget. You can do the frame-in-a-canvas solution which gives a lot of flexibility, but it's a bit more work. Using a text widget as a container doesn't give much flexibility with respect to layout, but it's very easy if all you need is a vertical stack of widgets.
Here is a working example:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
text = tk.Text(self, wrap="none")
vsb = tk.Scrollbar(orient="vertical", command=text.yview)
text.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
text.pack(fill="both", expand=True)
for i in range(20):
b = tk.Button(self, text="Button #%s" % i)
text.window_create("end", window=b)
text.insert("end", "\n")
text.configure(state="disabled")
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()

python 3.2.5 grid sticky/weight not working

Below is a simple illustration of the problem. It consists of a grid layout where I've placed a button widget in row 0 and a text widget in row 1.
What I want is for the text widget in row 1 to expand with the form while keeping the top of the text widget anchored NW (row 0 not expanding). The problem is that the horizontal(column expands correctly, but the text widget row does not. If I get rid of the button the text widget expands correctly. Also the original form I've put together is more involved and is best served using a grid. So basically using pack isn't a solution. Any help would be appreciated.
################################################
from tkinter import *
class Application(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initGui()
# ##################################################################
# Initialize GUI widgets
def initGui(self):
self.parent.title("Test Grid")
self.parent.resizable(width=TRUE, height=TRUE)
self.grid(sticky=W+E+N+S, padx=20, pady=20)
self.parent.columnconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
self.parent.rowconfigure(1, weight=1)
self.rowconfigure(1, weight=1)
# Add a button to row 0
self.btn = Button(self, text="Button", width=20)
self.btn.grid(row=0, column=0, padx=(0,10),pady=(0,10), sticky=N+W)
# Add a text box and v-scrollbar to row 1
self.txtOut = Text(self, width=80, height=20)
self.scrOut = Scrollbar(self)
self.txtOut.grid(row=1,column=0,padx=(0,18),sticky=N+E+S+W)
self.scrOut.grid(row=1,column=0,sticky=N+S+E)
self.scrOut.config(command=self.txtOut.yview)
self.txtOut.config(yscrollcommand=self.scrOut.set)
print(self.grid_size())
def main():
root = Tk()
app = Application(parent=root)
app.mainloop()
if __name__ == '__main__':
main()
There's a case where using pack probably is a solution. It won't cause any conflicts if you pack the instance of the Frame (Application) inside the root window, then grid widgets inside that frame. It'll cut down on the headache of all the rowconfigure and columnconfigure methods and just makes more sense to me.
def initGui(self):
self.parent.title("Test Grid")
self.parent.resizable(width=TRUE, height=TRUE)
self.pack(fill=BOTH, expand=1, padx=20, pady=20)
self.columnconfigure(0, weight=1)
self.rowconfigure(1, weight=1)
The core problem is that you are putting the application frame in row 0 (zero), but you are giving row 1 (one) a weight of 1 (one). That is why the text widget doesn't expand when you resize the window.
This is really hard to see because you mix grid commands of a widget in its parent, along with its own children widgets. It makes it hard to quickly scan the code to see what the layout options are, because you can't assume all of the grid, pack or place commands only affect child widgets.
Another minor problem with your code is that you've put the text widget and scrollbar in the same column.
Here's how I would change the code: remove the self.grid and self.parent.*configure commands from initGui. Then, use pack to add the application frame to the root window at the point where it's created. Also, move the scrollbar to column 1.
class Application(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initGui()
# ##################################################################
# Initialize GUI widgets
def initGui(self):
self.parent.title("Test Grid")
self.parent.resizable(width=TRUE, height=TRUE)
self.columnconfigure(0, weight=1)
self.rowconfigure(1, weight=1)
# Add a button to row 0
self.btn = Button(self, text="Button", width=20)
self.btn.grid(row=0, column=0, padx=(0,10),pady=(0,10), sticky=N+W)
# Add a text box and v-scrollbar to row 1
self.txtOut = Text(self, width=80, height=20)
self.scrOut = Scrollbar(self)
self.txtOut.grid(row=1,column=0,padx=(0,18),sticky=N+E+S+W)
self.scrOut.grid(row=1,column=1,sticky=N+S)
self.scrOut.config(command=self.txtOut.yview)
self.txtOut.config(yscrollcommand=self.scrOut.set)
print(self.grid_size())
def main():
root = Tk()
app = Application(parent=root)
app.pack(fill="both", expand=True)
app.mainloop()
if __name__ == '__main__':
main()

Categories