How to make widgets fit in screen - python

I cannot get my widgets fit in my computer screen and the layout is not forming as expected.
The two Text widgets should expand and occupy rest of frame available to them and the second frame which contains response2Field should fit into screen but it's not happening.
How can I achieve these goals?
# -*- coding: utf-8 -*-
from Tkinter import Tk,Label,Entry,Text,Button,Frame
text = """Python was created in the early 1990s by Guido van Rossum at Stichting
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
as a successor of a language called ABC. Guido remains Python's
principal author, although it includes many contributions from others."""
root = Tk()
request = Frame(root)
response = Frame(root)
response1 = Frame(response)
response2 = Frame(response)
request.grid(row=0,column=0,sticky='news')
response.grid(row=0,column=1)
response1.grid(row=0,column=0,sticky='w')
response2.grid(row=1,column=0,sticky='news')
InputText = Text(request)
InputText.pack(expand='true')
Button(response1,text='Submit',bg='brown',fg='yellow').pack()
response2Field = Text(response2,bg='black',fg='green')
response2Field.pack(expand='true')
InputText.insert("1.0",text)
response2Field.insert("1.0",text)
root.mainloop()
Output:

The default behaviors of tkinter geometry managers pack and grid is to grow containers and windows to display everything you put inside. If you want to constrain this, you can add at the end of your gui building code (copied from this other answer)
root.geometry('{}x{}'.format(<widthpixels>, <heightpixels>))
To have this to work, you have to handle correctly resizing in your layout. First of all, your use of grid is overelaborate, you do not have to use all these intermediate frames and can put your widgets directly in the grid. Second, grid need to be instructed of which row and columns should grow. This is done through the weight parameter. It describes the growth rate of an element (relative to the sum of all weights at the same level) and default to 0. This is configured on the container side. For instance, to have your request and response fill the entire height of the window, you have to add
root.grid_rowconfigure(0, weight=1)
On pack side, you have to specify both parameter expand and fill to have your widgets fill the entire available space pack(expand='true', fill='both').
To visualize re-size behavior of your Frame containers, you might consider to add borders borderwidth=2, relief='sunken' or background background='magenta' (it hurts the eyes, but helps to understand).
You can see that indeed InputText is not resizing, neither magenta request. response2Field occupies its whole green frame (misses fill='both' for proper resize handling, but not visible since part of the path which determine window original size).

Related

How to place a Tkinter widget at given coordinates in a fixed window?

Is it actually possible to place widgets at specific coordinates in a Tkinter window? For example, if i set up a window like so...
class LogInWindow(object):
def __init__(self):
#create variables
self.currentUser = StringVar()
#create the window and frame
self.LW = Toplevel()
self.LW.title('Login')
self.LW.geometry('310x100-500+300')
self.LW.resizable(width=False, height=False)
self.LWFrame = ttk.Frame(self.LW)
Creating a fixed window 310 pixels wide and 100 pixels high. How would I then place a button at say x=120,y=62?
I've explored the pack and grid documentation but cannot seem to find anything useful.
There's the less well known place geometry manager.
In your case you'd simply create the button and place it at the coordinates you want.
b = tk.Button(self.LW,text='Button')
b.place(x=120,y=62)
The reason people typical avoid 'place' is that it isn't automatically responsive to things like design changes or window resizes in the same way that pack and grid are.
You might be better off using relx and rely and the anchor options to express the position in terms of fractions of the window rather than specifying an absolute position to avoid some of these disadvantages.

How to Freeze a pane while re-sizing a window that has frames in python using tkinter and pack?

This is my first post on stackoverflow. I am finally posting because I can not find this anywhere and have been searching for nearly 4 hours, but I am stuck.
Here is my code example:
import tkinter as tk
from tkinter import *
root = tk.Tk()
root.geometry("600x100+200+200")
leftverticalFrame = Frame(root)
leftverticalFrame.pack(side=LEFT)
middleverticlFrame = Frame(root)
middleverticlFrame.pack(expand=TRUE)
rightverticalFrame = Frame(root)
rightverticalFrame.pack(side=RIGHT)
right = tk.Label(rightverticalFrame, text="Right Vertical Status Frame", bg="yellow")
right.pack(side=tk.RIGHT, fill=BOTH)
left = tk.Label(leftverticalFrame, text = "Left Vertical Navigation Frame", bg="orange")
left.pack(side=tk.LEFT, fill=BOTH)
bottom = tk.Label(middleverticlFrame, text="Middle Vertical Frame", bg="blue")
bottom.pack(side=tk.BOTTOM, expand=True, fill=tk.BOTH)
root.mainloop()
What I am doing is merely trying to layout the frames individually within the root because the frames will use different managers. The left frame is functioning exactly as I want it to, as is the middle frame. The problem is with the frame on the right.
Notice when you re-size the window making it more narrow, the right frame comes into the "middle frame's territory". Now the strange thing is the middle frame does not replicate the same behavior when it comes to the boundary of the left frame. I want the right frame to behave the same as the middle frame. Essentially I am trying to make the Left and Right fairly static, but the middle frame more dynamic. Can anyone tell me what I am doing wrong please?
An important thing to remember about pack is that the side attribute doesn't refer to the side of the window, it refers to the side of the remaining available space. The causes the order in which you pack things and the side that you pack them to be significant, because each time you pack something you change the location and amount of remaining available space.
In this case, the problem is that you didn't specify the side attribute for the middle frame, so it defaults to "top" (as in, "top of the remaining space", not "top of the window"). Since there's already something on the left, this puts it at the top of the remaining space on the right. Then, when you put the next item on the right, it's on the right but below the thing that is on the top.
There are at least a couple ways to solve this. The first is to pack the left and right sides first, and then pack the middle. In this case it doesn't matter which side you put the middle frame:
leftverticalFrame.pack(side=LEFT)
rightverticalFrame.pack(side=RIGHT)
middleverticlFrame.pack(expand=TRUE, side=TOP)
The second solution is to leave them in the original order, but pack the middle frame on the left or right instead of the top:
leftverticalFrame.pack(side=LEFT)
middleverticlFrame.pack(expand=TRUE, side=LEFT)
rightverticalFrame.pack(side=RIGHT)
These two variations will initially look identical, or perhaps nearly identical depending on what else might be in the frames or in the window. However, the behavior is different when you start to make the window too small to fit all of the frames.
In such a case, tkinter must eventually start reducing the size of a widget. It does this in the reverse order that they were packed (read: the last one to be packed is the first one to be shrunk). That means that if you want the left and right to be fixed as much as possible, you should pack the middle section last.
pro tip: it makes your code easier to read and maintain if you group all of your layout code together. Consider this code:
f1 = Frame(...)
f1.pack(...)
f2 = Frame(...)
f2.pack(...)
I think you'll find over time that your code is easier to read and maintain if you write it like this:
f1 = Frame(...)
f2 = Frame(...)
...
f1.pack(...)
f2.pack(...)
...
I think it makes the code much easier to visualize, since all of the layout for a given parent window is in one place rather than sprinkled throughout the code.

Tkinter box issues- Entry, Labelbox, Text all take up space from side to side, grid crashes computer

I'm using a tkinter canvas and trying to make a chat box on the right side of my game. However, I found that when I do...
import turtle
import tkinter as tk
master = tk.Tk()
w = tk.Canvas(master,width=1155,height=600,cursor='cross_reverse', bg='#101010')
shift = 1.000
sc = turtle.TurtleScreen(w)
tu = turtle.RawTurtle(sc)
e = tk.Entry(master, bg = '#000', fg = '#03f', font = 'Courier', justify='right', insertbackground = '#101010',width='115')
lb = tk.Listbox(master,height=3)
#e.grid(row=3,column=3)
sc.bgcolor("#101010")
txt = tk.Text(master,state="disabled")
txt.pack()
lb.pack()
w.pack()
sc.tracer(100)
drawcontinents() #Draws stuff with turtle, works just fine
e.pack()
tk.mainloop()
... a few things go wrong.
1.Text and Entry do not seem to want to coexist. I seem to be only able to have one or the other. My plan was to use entry as a chat entry, and display messages in Text. My backup plan is to append messages to label.
2.Text, entry, and Label box take up the entire window in whatever rows they are in, which blocks out the rest of what I am trying to draw. In other words,it puts the text box in the center, with a big gray stripe from side to side across whatever I've drawn. Is there any way to just display the box, and put it to the right?
3.Whenever I try to use the grid system, my whole computer freezes and I have to restart. Is this because the program is taking up more space than I have available, or is this a known bug or problem with installation?
You cannot use both pack and grid at the same time for the same containing widget (ie: for all widgets inside the same frame, toplevel or root window).
What happens is this: grid lays out all the widgets, potentially changing the size of some widgets based on your options (ie: it may grow a widget to stick to the sides of the cell). pack then notices that some widgets changed size in the containing widget it thinks it is responsible for, so it redoes what it thinks is the proper layout. This may change the size of some widgets based on your options. grid then notices that some widgets it thinks it is responsible for change size so it redoes what it does, potentially changing the size of some widgets. pack notices and re-adjusts, grid notices and re-adjusts, pack notices, ... until the end of time.
The solution is simple: only use grid, or only use pack, for all widgets that have a common parent. In this case, all your widgets share the root window as their parent, so they all need to use grid, or they all need to use pack.

Tkinter Canvas must be initialized before its contents. Is it a feature or a bug?

I just came across a strange behavior of Tkinter when debugging my program. If a Frame object is created before a Canvas object and later inserted into that Canvas, it can't be displayed. However if the creation order is inverted (firstly Canvas and then Frame), contents in the Frame is displayed correctly.
For example, the following code works well:
from Tkinter import *
app = Frame()
canvas = Canvas(app)
frame = Frame(app)
Label(frame, text = 'aaaa').pack()
Label(frame, text = 'bbbb').pack()
canvas.create_window(0, 0, anchor = NW, window = frame)
canvas.grid()
app.grid()
app.mainloop()
But if the initialization order is inverted, like:
frame = Frame(app)
canvas = Canvas(app)
you get nothing but a blank window.
Is this a intentionally designed behavior (If so, why?), or I just found a bug in Tkinter?
It is a feature. Widgets have a stacking order that defaults to the order that they were created. You can adjust this stacking order with the lift and lower methods.
For example, you can create the frame first and then the canvas, so that the canvas has a higher stacking order. As you observe, you don't see the frame because it is behind the canvas. To make it visible, you can lift it:
frame.lift(canvas)
Doing so will give the same visual effect as if you had created the canvas first.
This technique can be useful to hide and show widgets. For example, you can create a notebook-like widget by stacking several frames on top of each other, and then using lift to bring the one you want to be visible to the top of the order.

How to pack a tkinter widget underneath an existing widget that has been packed to the left side?

I'm attempting to write a basic Tkinter GUI that has a Text widget at the top, then a Button widget left aligned under it, then another Text widget underneath the button. The problem I'm having is, after packing the Button widget to the left, when I then go to pack the second Text widget, it puts it next to the button on the right, rather than underneath the button. This happens regardless of what I set the side argument to for the second Text widget Here's a simple piece of code that demonstrates this behaviour:
from Tkinter import *
root = Tk()
w = Text(root)
w.pack()
x = Button(root, text="Hi there!")
x.pack(side=LEFT)
y = Text(root)
y.pack(side=BOTTOM)
root.mainloop()
So how would I go about setting up the second Text widget so that it appears below the button, rather than to the right of it?
There are generally two solutions to layout problems:
switch to using grid. It becomes real easy to do layouts like what you are trying to accomplish. Grid can solve probably 95% of all layout issues (it's amazing when you think about it -- Tk does with one manager what most toolkits need half a dozen to accomplish!)
use multiple frames. If some widgets need to be stacked top-to-bottom and some left-to-right you can't always get what you want packing everything in a single frame. Use one frame for the top-to-bottom parts of the layout and additional frames for the left-to-right content.
Also realize that widgets don't have to be children of the widget in which they are packed/gridded. You can use the "in" parameter to put widgets in some other container than their parent.
For example, in your specific example you can create three frames, top, middle, bottom. Pack these top-to-bottom in your toplevel window. Then you can pack the first text widget in the top, the button or buttons horizontally in the middle, and the other text widget in the bottom.
The advantage to such an approach is that it makes it much easier to change the layout in the future (which in my experience always happens at some point). You don't have to re-parent any of your widgets, just pack/place/grid them in some other container.
In your short example it doesn't make much difference, but for complex apps this strategy can be a life saver.
My best advice is this: layout isn't an afterthought. Do a little planning, maybe even spend five minutes drawing on some graph paper. First decide on the major regions of your app and use a frame or some other container for each (paned window, notebook, etc). Once you have those, do the same divide-and-conquer approach for each section. This lets you use different types of layout for different sections of your app. Toolbars get horizontal layout, forms might get vertical layout, etc.
I was initially misunderstanding how packing worked and didn't realise that the entire left side was being "claimed" when i did x.pack(side=LEFT). What I found after reading this and the answer by Alex here is that I was not really after having x packed to the left side at all, but rather having it anchored to the left, using anchor=W (W for West) instead of side=LEFT. My revised code snippet which does what I was after looks like this:
from tkinter import *
root = Tk()
w = Text(root)
w.pack()
x = Button(root, text="Hi there!")
x.pack(anchor=W)
y = Text(root)
y.pack(side=BOTTOM)
root.mainloop()
This way x is not "claiming" the left side anymore, it's just aligned to the left (or West) within its block of space.
Packing happens in the order the .pack methods are called, so once x has "claimed" the left side, that's it -- it will take up the left portion of its parent and everything else within its parent will be to its right. You need a Frame to "mediate", e.g....:
from Tkinter import *
root = Tk()
w = Button(root, text="Mysterious W")
w.pack()
f = Frame(root)
x = Button(f, text="Hi there!")
x.pack()
y = Button(f, text="I be Y")
y.pack(side=BOTTOM)
f.pack(side=LEFT)
root.mainloop()
(changed Texts to Buttons for more immediate visibility of layout only -- the Tkinter on this Mac doesn't show Texts clearly until they have focus, but Buttons are quite clear;-).
Do it the same way that WebView does using the Mosaic Canvas Widget Sets internals(which are very similar to Tk). The trick is that the second identical named Frame Object works as a Block Level Float(inline:block;) for everything placed after it and everything that calls "fr" already will automatically begin over inside of it.
You can have many doing this of TOP aligned widgets and simply add another identical named Frame where you want to break between side=LEFT's. Works after Bottom also.
fr=Frame(root)
fr.pack(fill=X, side=TOP)
block1=Label(fr)
block1.pack(side=LEFT)
block2=Label(fr)
block2.pack(side=LEFT)
block3=Button(fr)
block3.pack(side=LEFT)
# NAME IT THE SAME ID NAME AS THE FIRST MAIN FRAME...
fr=Frame(root)
fr.pack(fill=X, side=TOP)
# These NOW jump into the second Frame breaking the side=LEFT in new Frame
block4=Label(fr)
block4.pack(side=LEFT)
block5=Label(fr)
block5.pack(side=LEFT)
# AND THEY CONTINUE GOING side=LEFT AFTERWARDS.

Categories

Resources