tkinter - Expanding a Listbox inside a Notebook - python

As the title says, I'm trying to make a Listbox object occupy all space that is inside a Notebook pane.
Here is a screenshot of what I have so far:
What I want to do is to expand the Listbox to occupy all the space that's on the right. I know that if I say listbox['width'] = value to a high enough value, the Notebook panes' titles will not bug this. However I am looking for alternative ways to do this, either resorting to grid() (what I'm using in this project) or pack().

If you're using pack, do it like this:
scrollbar.pack(side="right", fill="y")
listbox.pack(side="left", fill="both", expand=True)
If you're using grid, make sure you call columnconfigure on column 0 and give it a weight that is greater than zero, which will cause it to expand and fill up any extra space in the container.
containing_frame.grid_columnconfigure(0, weight=1)

Try putting sticky = E for the scroll bar grid and then try sticky = EW in the list box.
If that does not work, trying playing around with the column spans of the widgets.

Related

Scrollbar adding padding tkinter

I'm trying to place scrollbars inside a tkinter listbox, but every time I add them a huge amount of space is added to the top of the master window.
Below are a few snippets from my app. The first just generates the listbox and, as you can see from the first linked image below, the result is a nice, flush window. However, when I add the scrollbars from the second snippet, large amounts of space are placed at the top of the window.
#Create a listbox and fill with the data found in a dictionary called "d"
self.dataDisplay = Listbox(root, font = ("helvetica, 14"))
self.subfields = list(d.keys()) #d is a dictionary that is built elsewhere
for i in range(len(self.subfields)): #iterate through each dictionary item
dataType = type(d[self.subfields[i]])
self.dataDisplay.insert(END, str(helpers.displayHandler(self, self.subfields[i], str(dataType)))) #fill the listbox using a helper function called displayHandler that simply formats the data as a string
self.dataDisplay.pack(fill = "both", expand = True)
The above code works great and, along with the rest of the code in my app, displays a window that looks like this:
No scrollbars, no extra padding
Adding the following few lines of code to put scrollbars in the listbox, causes a huge amount of padding to be added to the top of the window.
scrollbarY = Scrollbar(self.dataDisplay, orient=VERTICAL)
scrollbarY.pack(side=RIGHT, fill=Y)
scrollbarX = Scrollbar(self.dataDisplay, orient=HORIZONTAL)
scrollbarX.pack(side=BOTTOM, fill=X)
self.dataDisplay.config(xscrollcommand = scrollbarX.set)
scrollbarY.config(command=self.dataDisplay.yview)
scrollbarX.config(command=self.dataDisplay.xview)
The result is the following:
Scrollbars, extra padding
I know that not being able to provide all the code in my app may limit possible answers, but I'm hoping someone can at least explain why simply adding scrollbars to the listbox widget can effect the properties of other parts of the app. Thanks!

Tkinter grids inside grids?

I would really appreciate some help with figuring out grid geometry manager.
Here is what I want to build.
I was thinking of using grid but I cannot find any good tutorials that would clearly
explain how to work with it.
There are lots of tutorials but mostly all are either very simple or really outdated.
I am not sure how to build what is shown in the picture using only grid because all
elements are nested inside each other and each element is supposed to hold more elements inside it.
It's not so hard to arrange outermost widgets using grid. I just place Toolbar into 0th row,
then outermost PanedWidow (green) into 1st row, and then Status Bar into 2nd row.
After that I need to arrange things inside green PanedWindow.
I place another PanedWindow (pink) into the right pane of the green PanedWindow and then
stick a Notebook into it's top pane.
Now, I need to add more widgets to these inner panes. For instance. I am going to add
some buttons to the bottom pane of the pink PanedWindow. And that's where I run into problems.
If I try to use pack() to arrange things inside these innermost panes, Python screams at me for
using more than one geometry manager.
But when I think about how to accomplish this with grid, I just can't find a way to subdivide
innermost panes into smaller grids.
Can there be grids inside Widgets which have been acted upon by an outer grid?
When I see widgets that take up the full width or full height of an area I usually use pack since since it's specifically designed to lay objects along a side of an empty cavity. You can use grid but it requires extra code since you have to both add the widget and configure the rows and columns. With pack all you have to do is add the widgets.
For example, it's clear you want a statusbar along the bottom, and a toolbar along the time, and a paned widget in-between. So, start with that, as in the following example:
import tkinter as tk
root = tk.Tk()
root.geometry("600x400")
toolbar = tk.Frame(root, background="#d5e8d4", height=40)
statusbar = tk.Frame(root, background="#e3e3e3", height=20)
main = tk.PanedWindow(root, background="#99fb99")
toolbar.pack(side="top", fill="x")
statusbar.pack(side="bottom", fill="x")
main.pack(side="top", fill="both", expand=True)
root.mainloop()
Note: widths, heights, and colors are added to the frame for illustrative purposes since otherwise, an empty frame would have a size of 1x1. Once you add widgets inside a frame you can remove the width and height options.
You say the right will have a paned window, so add that on the right. We'll use a normal frame on the left.
left_pane = tk.Frame(main, background="#99fb99", width=100)
right_pane = tk.PanedWindow(main, background="#99fb99", width=200)
main.add(left_pane)
main.add(right_pane)
Next, add the two panes to the right. So that I can show colors with as little code as possible I'll use a frame on the top instead of a notebook:
notebook = tk.Frame(right_pane, background="#99ceff", height=70)
bottom_right = tk.Frame(right_pane, background="#ffe6cd", height=50)
right_pane.add(notebook)
right_pane.add(bottom_right)
With all that being said, you can use grid if you want. The trick is to use intermediate frames, since the layout in any widget is independent of the layout in parent or child widgets.
All you need to do is remove the first three calls to pack and replace it with these five lines:
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
toolbar.grid(row=0, column=0, sticky="ew")
main.grid(row=1, column=0, sticky="nsew")
statusbar.grid(row=2, column=0, sticky="ew")
Since the other widgets are children of paned widgets, there's nothing else to do. Any widgets you add to each pane have their own independent layout area, so you can use grid, pack, or place inside each frame.
To illustrate that point, I'll use grid to add several rows and columns of squares:
for row in range(6):
for column in range(30):
f = tk.Frame(bottom_right, background="white",
bd=2, relief="raised", width=10, height=10)
f.grid(row=row, column=column)
I found a post that's around two years old, which might be a little too old for your uses, but it has some information on nesting grids in Tkinter. It recommends using frames to nest the grids, essentially having children within children of a frame. Within these frames, you can place objects.

TKInter - The text in the label changes window's width

I'm building a calculator in python, using the tkinter toolkit. The problem is that when the text is typed in the calculator (and added to the label that shoes the calculation) the whole width of the window changes and becomes longer. What can I do in order to solve it?
Here is some important parts of the code: (the main class inherits from tk.Frame)
labelStyle = {"padx":10, "pady":10, "justify":"left"}
calculationsLabel = tk.Label(self, text="", **labelStyle)
calculationsLabel.grid(row=0, column=1, **gridStyle)
self.master.resizable(0,0)
self.pack(padx=5, pady=5)
self.calculationsLabel=calculationsLabel
for number in range(9,0,-1):
...
tk.Button(self, ...).grid(...)
The images that illustrates the problem:
You need to do a couple of things to prevent the label from changing the window size:
Give the label a width of 1 (one). By not giving the label a width you are implicitly saying "grow bigger when the text is bigger". By giving it an explicit width you are saying "no matter how big the text is, don't grow if the text is bigger".
use the columnspan option to grid, to get the cell that the label is in to span the width of your grid of buttons. Use the sticky option, so that even though the label requests only one character of width, grid will force it to grow wide enough to fit the space.
You can use the columnspan argument to let the label stretch over top all of your columns, instead of stretching out the first one. Add to your grid method:
calculationsLabel.grid(row=0, column=1, columnspan=7 **gridStyle)
(replacing 7 with however many columns you actually have)
You could use the columnspan option of grid to make the label span the complete width of the calculator.
Then, if you want the number to be on the left use anchor=W in the Label.
This way the window only expands when the Label is longer than the complete window. And you can even prevent that by using the Label's width option.

Stretching frames using grid layout in Python Tkinter

I'm trying to get stretching to work using Python 2.6.7 with Tkinter. I'd expect the below code to stretch the first button to the width of the second, but both buttons are only as wide as they need to be to fit their text.
#!/usr/bin/python
from Tkinter import *
win = Frame()
win.grid(sticky=N+S+E+W)
inner_a = Frame(win)
inner_a.grid(row=0, column=0, sticky=N+E+W)
inner_b = Frame(win)
inner_b.grid(row=1, column=0, sticky=S+E+W)
Button(inner_a, text='1').grid(row=0, column=0, sticky=E+W)
Button(inner_b, text='Some long text').grid(row=0, column=0, sticky=E+W)
win.mainloop()
By my understanding, the single column in win will expand to the width of the largest thing it contains, ie the width of inner_b, and then the width of inner_a, and thence of the first button, will be that of the second button.
Actually, what happens is the below; the first button is only wide enough to contain "1", not as wide as the second button.
What do I need to do to get the first button to expand the size of the second?
If you want widgets to line up in a grid, the first thing to do is make sure they have the same parent. This isn't strictly necessary if all the widgets are the same size or you are only using one column.
Another thing you need to do is give your column a weight. What is happening in your code is that the widget is expanding to fill the column, but the column isn't expanding to fill the master. If you give it a weight of 1 it will. You ned to do something like inner_a.columnconfigure(1, weight=1), and then do likewise for inner_b.

Randomly add buttons to Tkinter GUI?

How do I randomly add buttons to a Tkinter GUI? I need it to be able to create a button, then put it anywhere on the window, is this possible? I am using Python 2.6 on Windows.
If you want random button placement (or anything not aligned along a grid, etc.), you can use the place geometry manager. Depending on platform, overlapped buttons may not behave as you expect, though, so you may want to avoid them.
Here's a simple example:
from Tkinter import *
from random import random
root = Tk()
frame = Frame(root, height=200, width=200)
for i in range(10):
Button(frame, text=str(i)).place(x=random() * 150, y=random() * 180)
frame.pack()
root.mainloop()
There are several options to choose from. For example, you could design on a grid where you have six buttons per row. Then it's just a matter of starting at row 0, incrementing the column for each button. When you get to the last column, reset the column to 0 and increment the row by one.
Another option is to use a text widget as the container, and embed your buttons in the text widget with wrapping enabled. With this trick the buttons will fill a row automatically and wrap if the user grows or shrinks the main windows. It's a tiny bit more work, but it works well if that's the behavior you want.

Categories

Resources