Conceptually, if I define a Frame and create it using, say, grid, then within Frame can I use any of the geometry managers? Below is a MWE. My problem is I am trying to write a GUI where the base layout is with grid, but within those widgets I am having loads of trouble setting objects. I think it may be because I am passing through modules. I am aware of this answer here, and it sheds some light, but if I pass a tk Frame object to a class, do a bunch of Tk stuff to it, how is it being handled in terms of geometry management?
Specifically, say I have a base gui layout:
import Tkinter as tk
from Tkinter import Frame
from modules.subfolder.name_of_file import SomeClass
class Window(tk.Frame):
def __init__(self, master):
self.master = master
self.configure_gui()
self.create_widgets()
self.create_modules()
def configure_gui(self):
self.master.wm_attributes('-fullscreen','true')
self.master.configure(background='black')
# in pixels
self.screen_width = self.master.winfo_screenwidth()
self.screen_height = self.master.winfo_screenheight()
# max height for header and footer
self.foot_height = 100
self.header_above_height = 100
self.header_below_height = 100
def create_widgets(self):
# Main layout - like a document, header footer, main
self.header_above = Frame(self.master, bg='black', width = self.screen_width, height=self.header_above_height)
self.header_below = Frame(self.master, bg='black', width = self.screen_width, height=self.header_below_height)
self.center = Frame(self.master, bg='black', width=self.screen_width)
self.footer = Frame(self.master, bg='black', width = self.screen_width, height = self.foot_height)
# this makes row 1 grow first so it will push out
# to the top and bottom
self.master.grid_rowconfigure(2, weight=1)
self.master.grid_columnconfigure(1, weight=1)
self.header_above.grid(row=0, sticky="ew")
self.header_below.grid(row=1, sticky="ew")
self.center.grid(row=2, sticky="nsew")
self.footer.grid(row=3, sticky="ew")
def create_modules(self):
# Module
self.sub_widget = SomeClass(self.header_above)
self.sub_widget.pack()
def main():
root = tk.Tk()
gui = Window(root)
root.mainloop()
if __name__ == '__main__':
main()
Often, I am not getting the expected behavior in the self.sub_widget.pack(). Let's say I wanted to pack in two Labels and have them right adjusted, so against the right side of the screen back in its parent frame header_above. How can I acheive this. Because now it comes back left-adjusted.
from Tkinter import *
from PIL import Image, ImageTk
class SomeClass(Frame):
def __init__(self, parent, *args, **kwargs):
Frame.__init__(self, parent, bg='black')
self.config(bg='black')
image = Image.open('images/Moon.png')
image = image.resize((100, 100), Image.ANTIALIAS)
image = image.convert('RGB')
photo = ImageTk.PhotoImage(image)
self.display_icon = Label(self, image=photo, width=100, height=100)
self.display_icon.pack(side=RIGHT)
self.display_name = Label(self,text="name to display",bg='black',fg="white")
self.display_name.pack(side=TOP)
Conceptually, if I define a Frame and create it using, say, grid, then within Frame can I use any of the geometry managers?
Yes, you can. Every widget can use whatever geometry manager it wants to manage its own children, independent of what the rest of the widgets use.
The fact that are or aren't using modules is totally irrelevant.
Related
I have a GUI where I need different tkinter frames to have different window sizes due to what is contained within them. Is there a way to explicitly set the size of these frames, or size them relative to what is contained within them?
Hi Kane_Iskra there are two main ways of doing what you want.
By using the right frame placement method
By resizing the window
To relatively place an item inside a frame you have to use a pack or grid method of placement. For example.
Pack
frame1 = tk.frame(root, width=30,height=30)
frame1.pack(side='top')
Grid
frame1 = tk.frame(root, width=30,height=30)
frame1.grid(row='top')
# set weights to the frame so when you resize the window the frame size changes
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
Or you can also change the size of the window by using
root.geometry('1000x540')
I once did the example with classes because you wrote in the text above my post that you were looking for such an example.
If you want to change your windows dynamically, place is a very good option (grid is also possible, of course). The disadvantage of this is that if you change one size, you mostly have to change it for another frame (but it is not always the case). And you have to constantly experiment with the numbers of rely, relx ....
from tkinter import *
class MainWindow(Tk):
def __init__(self, parent=None):
super().__init__()
self.parent = parent
#=> your class imports:
# toolbar:
self.toolBar = ToolBar(self)
self.toolBar.place(relx=0, rely=0, relwidth=1, relheight=0.1)
# main
self.main = Main(self)
self.main.place(relx=0, rely=0.1, relwidth=1, relheight=0.9)
class ToolBar(Frame):
def __init__(self, parent):
Frame.__init__(self, bg="light blue") # you can set your frame attributes here
self.parent = parent # your reference to the other classes, for example (if you
# want to communicate with class Main: self.parent.main.widget.....)
class Main(Frame):
def __init__(self, parent):
Frame.__init__(self, bg="light green", highlightthickness=10) # frame attributes
self.parent = parent # reference
btn = Button(self, text="CloseApp", bg=self.cget("bg"), command=self.parent.destroy) #exit button
btn.place(relwidth=0.3, relheight=0.3, rely=0.35, relx=0.35) # self.parent.destroy reference to you MainWindow (to destroy)
if __name__ == '__main__':
mw = MainWindow()
mw.geometry("750x500")
mw.mainloop()
I am wanting to create a grid layout, with a grid that fills the first row until it runs out of space in the window, and will dynamically move items to the row below (like text line-wrapping). As the window width is adjusted, the grid adjusts to fit. The boxes resizing is not desired. I intend to maintain each small box's size, but change where the layout puts each box.
I imagine this functionality is possible by measuring the width of the frame, and if the (number of boxes)*(width of each box) exceeds the width, move to the next row. I was just wondering if there was a better way built in that I'm not understanding.
If the above is the only option, what is the best way to update that? Do I have to set an event on window resize or something? It seems like I shouldn't have to rework a layout manager, which is what that feels like. I just want to check if similar functionality is already built in. Grid seems like a powerful layout manager, but I have not been able to find that option.
The below pics describes the behavior I want using the same set of 6 boxes on a single frame using grid layout.
Window is wide enough to hold all 6 boxes, so they all fit on row 1. They then adjust as window size changes.
If you plan on forcing each box to be a uniform size, the simplest solution is to use the text widget as the container since it has the built-in ability to wrap.
Here is a working example. Click on the "add" button to add additional boxes. Resize the window to see that they automatically wrap as the window grows and shrinks.
import Tkinter as tk
import random
class DynamicGrid(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.text = tk.Text(self, wrap="char", borderwidth=0, highlightthickness=0,
state="disabled")
self.text.pack(fill="both", expand=True)
self.boxes = []
def add_box(self, color=None):
bg = color if color else random.choice(("red", "orange", "green", "blue", "violet"))
box = tk.Frame(self.text, bd=1, relief="sunken", background=bg,
width=100, height=100)
self.boxes.append(box)
self.text.configure(state="normal")
self.text.window_create("end", window=box)
self.text.configure(state="disabled")
class Example(object):
def __init__(self):
self.root = tk.Tk()
self.dg = DynamicGrid(self.root, width=500, height=200)
add_button = tk.Button(self.root, text="Add", command=self.dg.add_box)
add_button.pack()
self.dg.pack(side="top", fill="both", expand=True)
# add a few boxes to start
for i in range(10):
self.dg.add_box()
def start(self):
self.root.mainloop()
Example().start()
Here's a working example:
import Tkinter as tk
class AutoGrid(tk.Frame):
def __init__(self, master=None, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
self.columns = None
self.bind('<Configure>', self.regrid)
def regrid(self, event=None):
width = self.winfo_width()
slaves = self.grid_slaves()
max_width = max(slave.winfo_width() for slave in slaves)
cols = width // max_width
if cols == self.columns: # if the column number has not changed, abort
return
for i, slave in enumerate(slaves):
slave.grid_forget()
slave.grid(row=i//cols, column=i%cols)
self.columns = cols
class TestFrame(tk.Frame):
def __init__(self, master=None, **kwargs):
tk.Frame.__init__(self, master, bd=5, relief=tk.RAISED, **kwargs)
tk.Label(self, text="name").pack(pady=10)
tk.Label(self, text=" info ........ info ").pack(pady=10)
tk.Label(self, text="data\n"*5).pack(pady=10)
def main():
root = tk.Tk()
frame = AutoGrid(root)
frame.pack(fill=tk.BOTH, expand=True)
TestFrame(frame).grid() # use normal grid parameters to set up initial layout
TestFrame(frame).grid(column=1)
TestFrame(frame).grid(column=2)
TestFrame(frame).grid()
TestFrame(frame).grid()
TestFrame(frame).grid()
root.mainloop()
if __name__ == '__main__':
main()
Note this will ruin the rowspan and columnspan features of the grid manager.
Here's a streamlined version of Bryan's answer without classes and a few extra comments for anyone who is confused and is trying to implement this quickly into their own project.
from tkinter import *
import tkinter as tk
#Create main window
root = tk.Tk()
#Create WidgetWrapper
widgetWrapper = tk.Text(root, wrap="char", borderwidth=0,highlightthickness=0,state="disabled", cursor="arrow")
#state = "disabled" is to disable text from being input by user
#cursor = "arrow" is to ensure when user hovers, the "I" beam cursor (text cursor) is not displayed
widgetWrapper.pack(fill="both", expand=True)
def additem():
item = Label(bd = 5, relief="solid", text="O", bg="red") #Create the actual widgets
widgetWrapper.window_create("end", window=item) #Put it inside the widget wrapper (the text)
# add a few boxes to start
for i in range(10):
additem()
#Not needed to implement in other code, just an add button
add_button = tk.Button(root, text="Add", command=additem)
add_button.pack()
How do I place the QUIT button in below code to the extreme right of the Frame?
I tried several things like:
padx
and
self.pack(side="top", anchor="e")
but after trying some 15 times both buttons are coming close to each other. Maybe Some help from anyone would be really appreciated. I need one button on extreme right and other on extreme left
import tkinter as tk
from tkinter.ttk import *
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse.."
# self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.Sayhello
self.Run_Main.pack(side='left')
self.Label = tk.Label(self)
self.Label["text"] = 'Processing...'
self.progressbar = Progressbar(mode="indeterminate", maximum=20)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["command"] = self.quit
self.QUIT.pack(anchor='e')
self.pack(side="top", anchor="w")
def Sayhello(self):
print("Hello")
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=18, width=60)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
return
root = tk.Tk()
root.resizable(width=False, height=False)
root.option_add('*font', ('verdana', 9, 'bold'))
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
You have several problems here.
First, you're using the wrong geometry manager. The pack geometry manager, as the name implies, packs the widgets as close together as possible. That's not what you want. The grid geometry manager lets you put the widgets into a table-like layout with rows and columns. If you put the Browse button into the first column and the Quit button into the last column, you'll be a step closer.
Second, your Application window contains three child widgets and you're only putting two of them into a geometry manager. How that is going to mess you up I don't even want to think about. So I put the label into column 1, the Quit button into column 2, and the Browse button into column 0. The Quit button I gave a "sticky" value of "e" so it will be attached to the east (right) side of its allocated space.
Third, all the geometry managers try to compact the widgets as much as possible unless you specifically tell it to do otherwise. I told the grid manager to expand column 2 so that the extra space gets assigned to the cell that holds the Quit button.
Fourth, you need to tell the pack manager to expand the top widget so that it spans the entire window. The directive for that is fill="x".
Fifth, you have a redundant call to the pack manager at the end of your createWidgets function.
import tkinter as tk
from tkinter.ttk import *
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack(fill="x")
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse.."
# self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.Sayhello
self.Label = tk.Label(self)
self.Label["text"] = 'Processing...'
self.progressbar = Progressbar(mode="indeterminate", maximum=20)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["command"] = self.quit
self.Label.grid(row=0, column=1)
self.Run_Main.grid(row=0, column=0, sticky="w")
self.QUIT.grid(row=0, column=2, sticky="e")
self.columnconfigure(2, weight=1)
def Sayhello(self):
print("Hello")
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=18, width=60)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
return
root = tk.Tk()
root.resizable(width=False, height=False)
root.option_add('*font', ('verdana', 9, 'bold'))
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
These link, link helped. The other option would be to use tkinter's grid manager, it will be more intuitive and keep you more organized in the future.
import tkinter as tk
from tkinter.ttk import *
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse.."
# self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.Sayhello
self.Run_Main.pack(side='left')
self.Label = tk.Label(self)
self.Label["text"] = 'Processing...'
self.Label.pack(side='left')
self.progressbar = Progressbar(mode="indeterminate", maximum=20)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["command"] = self.quit
self.QUIT.pack(side='right')
self.pack(side="top", fill=tk.BOTH) # changes here
def Sayhello(self):
print("Hello")
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=18, width=60)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
return
root = tk.Tk()
root.resizable(width=False, height=False)
root.option_add('*font', ('verdana', 9, 'bold'))
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
There are two simple fixes you can make in order to get the behavior you want.
First, you need to pack Application so that it fills the window:
class Application(...):
def __init__(...):
...
self.pack(fill="x")
Next, simply pack the quick button on the right side of the window:
self.QUIT.pack(side="right", anchor='e')
Even though the above is all you need to do in this specific example, there are additional things you can do to make your job much easier.
I would recommend creating a frame specifically for the buttons. You can pack it at the top. Then, put the buttons inside this frame, and pack them either on the left or right. You'll get the same results, but you'll find it easier to add additional buttons later.
I also find that it makes the code much easier to read, write, maintain, and visualize when you separate widget creation from widget layout.
class Application(...):
...
def createWidgets(self):
toolbar = tk.Frame(self)
toolbar.pack(side="top", fill="x")
self.Run_Main = tk.Button(toolbar)
self.Label = tk.Label(toolbar)
self.QUIT = tk.Button(toolbar)
...
self.Run_Main.pack(side="left")
self.Label.pack(side="left", fill="x")
self.QUIT.pack(side="right")
...
I am trying to develop a Python Tkinter widget that opens a window displaying a photo and allows the user to scroll through the photos using a scroll bar. For context, the idea is to be able to scroll through a timer-series of photographs.
So far I've been able to make a Tkinter canvas displaying an image, and a Tkinter "Scale" slider, but I am having trouble combining them to meet my goal. The result of the below code is an empty canvas window and a separate slider. The slider works and prints its position when moved, but no photo ever loads. I was hoping that when the bar was moved to position 3, photo 3 would load.
Any idea what I'm missing?
import Tkinter as tk
from PIL import ImageTk, Image
from Tkinter import *
class ImageCanvas(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.canvas = Canvas(self, width=720, height=480, bd=0)
self.canvas.grid(row=0, column=0, sticky='nsew', padx=4, pady=4)
self.root = tk.Tk()
self._job = NONE
self.slider = tk.Scale(self.root, from_=0, to=3, orient = "horizontal", command=self.updateValue)
self.slider.pack()
self.root.mainloop()
def updateValue(self, event):
if self._job:
self.root.after_cancel(self._job)
self._job = self.root.after(500, self.result)
def result(self):
self._job=None
print self.slider.get()
returnedValue = self.slider.get()
class ImgTk(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.main = ImageCanvas(self)
self.main.grid(row=0, column=0, sticky='nsew')
self.c = self.main.canvas
self.currentImage = {}
self.load_imgfile(images[ImageCanvas.returnedValue()])
def load_imgfile(self, filename):
self.img = Image.open(filename)
self.currentImage['data'] = self.img
self.photo = ImageTk.PhotoImage(self.img)
self.c.xview_moveto(0)
self.c.yview_moveto(0)
self.c.create_image(0, 0, image=self.photo, anchor='nw', tags='img')
self.c.config(scrollregion=self.c.bbox('all'))
self.currentImage['photo'] = self.photo
images = ['/Users/Evan/Documents/Temp/4744.png', '/Users/Evan/Documents/Temp/4750.png', '/Users/Evan/Documents/Temp/4757.png']
app = ImgTk()
A couple suggestions may get you on the right track. Move the self.root.mainloop() out of the ImageCanvas class and put it at the end, after app = ImgTk(), as simply mainloop(), so it only get's called ones per instance of your app.
Also, ImageCanvas class doesn't have a method called returnedValue, and the result method doesn't return any value. So, add return returnedValue to the result method. Then ImageCanvas.returnedValue() would need to be ImageCanvas.result(), or you could just say self.main.result().
After making these changes, the first image displays, more work will need to be done to get the other images to load.
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()