Tkinter frame size changing after inserting a widget in it - python

I have some code that is supposed to put a label into a frame that is set to a certain size. But when it inserts it, the frame shrinks to the smallest size that is just big enough to fit the label in. What am I doing wrong?
Here's my code for the frame:
problem_fr = LabelFrame(root,text="Solve this problem:",height=200,width=400)
problem_fr.grid(row=0,column=0,columnspan=3)
Here's my code to put the label into the frame:
def erase_widgets(frame):
for widgets in frame.winfo_children():
widgets.destroy()
def draw_problem(problem):
erase_widgets(problem_fr)
curr_prob = Label(problem_fr,text=problem)
curr_prob.grid(row=0,column=0)

Related

Tkinter scrollbar isn't reacting to the size of my canvas

I'm trying to implement a scrollbar that will scroll through a frame in a canvas window. The canvas, frame, and frame elements are all functioning well as far as I can see. I have no issues with adding or removing elements from the frame, and they show up as I want them to. The only issue is with the scrollbar.
The scrollbar doesn't work at all. It shows up exactly where it should, but it doesn't react at all to the canvas or frame elements. If I resize the window and squish down the canvas, the scrollbar does nothing — it just stays empty as though there's nothing for it to scroll through.
I recognize there are a million other questions about scrollbars in canvases, but I've carefully read through as many of them as I could and didn't find any sort of solution to my problem. So with that, I ask: any idea where I'm going wrong?
Any feedback or suggestions for possible fixes would be greatly appreciated.
lbl_frame = Canvas(menu, bg=root_bg, width=(menu.winfo_width() - 35), height=canvas_size, highlightthickness=0)
f = Frame(lbl_frame, bg=root_bg) # Frame that goes in the canvas
scroll = Scrollbar(menu, bg=root_bg, orient='vertical', width=17) # Scrollbar
scroll.grid(row=3, column=2, sticky='ns')
scroll.config(command=lbl_frame.yview) # Scrollbar's command reads information about the height of the canvas
lbl_frame.create_window(0, 0, window=f, anchor="nw") # Creating a canvas window for the frame
lbl_frame.config(yscrollcommand=scroll.set)
def canvas_resize(event):
global min_size # Currently set to 95
canvas_size = menu.winfo_height() - 170 # Sum of all other elements
if canvas_size < min_size:
canvas_size = min_size # Canvas size must be >= 100
lbl_frame.configure(height=canvas_size) # Scale canvas size with window
lbl_frame.configure(scrollregion=lbl_frame.bbox("all"))
# Set scrollregion to the total region of all elements in the frame
menu.bind("<Configure>", lambda e: canvas_resize(e))
Note that elements are added to the frame shortly after this. Because the process I add them by is very long and contains a lot of stuff that isn't relevant to my problem, plus I don't have any issues with adding these elements, I've decided not to include that part.
I figured it out. It was with how I was adding the elements. I added them to the canvas, not the frame. So the frame was left empty.

Button grid shrinking vertically when adding a certain amount of images

I'm making a chess board in Tkinter with chess piece images to get used to the module, and have success with placing the first N-1 images in a row in an NxN grid.
However, upon coding an Nth image on any row, the row in question shrinks vertically.
I'm trying to use an OOP approach and am thinking there may be a method i'm misusing or missing entirely.
I've tried adjusting the arguments for the grid() method such as row and column, but they don't appear to affect the result. Adjusting the width and height of the buttons did not do anything significant. I used a 4x3 grid as a testing example and the images fit with the grid if I leave an empty button without an image in a row.
import tkinter as tk
class ChessBoard(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self,parent,*args,**kwargs)
self.parent = parent
self.button_identities = [[[] for x in range(3)] for y in range(3)]
self.pictures = {"Rook":tk.PhotoImage(file="rook.png")}
self.initialize_board()
def initialize_board(self):
for r in range(3):
for c in range(3):
button = tk.Button(self.parent,bg="white",\
width=10,height=5)
button.grid(row=r,column=c,sticky="sewn")
self.button_identities[r][c] = button
self.setup_starting_positions()
def setup_starting_positions(self):
# Commenting out one of the lines in this method keeps the example row intact
# In this example I'm trying to make 3 images fit in a single row without the row shrinking
self.button_identities[0][0].config(image=self.pictures["Rook"])
self.button_identities[0][1].config(image=self.pictures["Rook"])
self.button_identities[0][2].config(image=self.pictures["Rook"])
class MainApplication(tk.Frame):
# The Main Application hosts the Chessboard frame
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self,parent,*args,**kwargs)
self.parent = parent
self.parent.geometry('{}x{}'.format(1000,800))
self.chessboard = ChessBoard(self)
self.chessboard.grid_propagate(False)
self.chessboard.grid(row=1,column=1,columnspan=3)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).grid(row=0,column=0)
root.mainloop()
I expected the buttons to show some sign of changing when I tried editing the grid() methods or width/height arguments when initializing a Button, but nothing remarkable seemed to change. I feel as though I'm not understanding how the Grid manager works in this case. Sorry for the lack of images.
In empty Button (or with text) height=5 means 5 lines of text. When you put image then it means 5 pixels.
When you have empty button in row then it has size 5 lines and it is the highest widget in row so other buttons are resized to row size. When you add last button then all buttons has height 5 pixels and there is bigger widget to resize row.
So you have to set width and height in pixels and put all images.
Or simple remove width, height from buttons and they will use width, height of image.
button = tk.Button(self.parent, bg="white")
See: Button

Create Textbox widgets in Tkinter of different length for multiple Labels in different rows

I have been trying to create an application using Tkinter module for Python 3.4.2
There are N number of labels.
For each of these N number of labels, I am trying to create a row of Textboxes which might/might not be of the same length.
The number of Textboxes for each label might be different too.
I need a scrollbar for this window.
So I created a canvas and used Scrollbar widget to create the horizontal and vertical scrollbar. In this canvas, I entered a frame so that I could add my labels and Textboxes.
To construct Textboxes of varying lengths I used grid() method for the widgets.
I read that the grid columns are of length enough to accommodate the largest widget(here it is a Textbox) and you can't add multiple widgets in a cell of the grid.
I used columnspan property of the Textbox widget but was of no help. e1.grid(row=i, column=1, columnspan=width)
for the first column
and e1.grid(row=i, column=width_sum, columnspan=width) for the subsequent columns. Here width_sum is the sum of length of different textboxes because of the present one. The width_sum parameter is reset for every Label (which is present in a different row).
I need output something like this:
enter image description here
Thanks in advance.
To realize the GUI you describe, I think your best option is to:
Create 1 frame per row.
Then in each frame, use the .pack() geometry manager to pack the textboxes side by side.
Code:
import tkinter as tk
from random import randint
root = tk.Tk()
# Create N frames on top of each other
N = 4
frames = []
for n in range(N):
frame = tk.Frame(root)
frame.pack(side='top', anchor='w')
# Store the current frame reference in "frames"
frames.append(frame)
# Add some widgets in each frame
entryboxes = {frame: [] for frame in frames}
for i, frame in enumerate(frames):
# Add a label
label = tk.Label(frame, text="Label "+str(i+1))
label.pack(side='left')
# Add 5 Entry boxes with random widths
for i in range(5):
random_width = 10 + randint(0,9)
e = tk.Entry(frame, width = random_width)
e.pack(side='left')
# Store the current entrybox reference in "entryboxes"
entryboxes[frame].append(e)
# Add some text in the 4th box of the 3rd frame
entryboxes[frames[2]][3].insert(0, 'hello')
# Launch the app
root.mainloop()

tkinter - create label widget (containing text) with size defined by pixel dimensions

I am trying to make a tkinter label containing text with width and height defined by pixel dimensions. It is made clear that a label widget containing text's size is defined in terms of its text size in the docs:
If the label displays text, the size is given in text units. If the label displays an image, the size is given in pixels (or screen units). If the size is set to 0, or omitted, it is calculated based on the label contents.
I have tried using this information to achieve what I am trying to do. I want to create labels of fixed width 700 and variable height as I am creating a timetable application. Here's an example of what I have tried so far:
import tkinter as tk
root = tk.Tk()
root.geometry("800x600")
height = 20
label = tk.Label(root,width=700,bg="white",text="test",borderwidth=0,font=("Calibri",height))
label.place(x=10,y=10)
root.mainloop()
This almost achieves what I want in terms of height, but I would expect the height to be 1 pixel when height = 1, but it is actually 5 pixels (I've got very good eyesight!). As for width, its completely off the screen as its 700 times the width of a character.
Using individual frames as each "label" and then creating label widgets as children of these frames does not work either, as the frame just gets resized to fit the label. My question is: is there a way to create a text-containing label sized by pixel dimensions?
I have found a solution in my case. I used a frame, disabled its pack_propagate and made a label the child of this frame. I hope others will find it useful. This question helped me a lot.
import tkinter as tk
root = tk.Tk()
root.geometry("800x600")
height = 20
label_frame = tk.Frame(root,width=700,height=height,bg="white")
label_frame.pack_propagate(0) # Stops child widgets of label_frame from resizing it
tk.Label(label_frame,bg="white",fg="black",text="test",font=("Calibri",15)).pack()
label_frame.place(x=10,y=10)
root.mainloop()
doing a timetable is something that lends itself to using the grid packing method, which has a grid_columnconfigure and a grid_rowconfigure method which can be used to set the minimum and maximum size of a row or column, and the rate at which they expand:
import tkinter as tk
root = tk.Tk()
tk.Label(root, text="Test").grid(column=1, row=1, sticky="nesw")
root.grid_columnconfigure(1, minsize=700)
root.mainloop()

Tkinter Frame Resize one axis

I have a tkinter frame that I want to automatically resize on the Y axis but remain a constant width as I add label widgets to it.
To keep the X-size constant I am using grid_propogate(False) but that keeps the whole thing a constant size.
How can I set the frame to resize in this manner?
Thanks
Ok, figured it out
Made a larger frame that encompassed the space that my frame could fill if maxed out and used pack(fill=X) and pack_propagate(False) to make the inner frame conform to the X dimension of the outer frame while not changing it. I then could add lines to the innerframe as needed with it maintaining it's X size:
OuterFrame = Frame(root, height=500, width=400)
InnerFrame = Frame(OuterFrame, borderwidth=3, relief=RAISED)
InnerFrame.pack(side=TOP, fill=X)
# stuff that goes in the y-resizing InnerFrame
OuterFrame.pack_propogate(False)
OuterFrame.pack()
This seems like a very ugly solution to me, (hopefully someone will come along with something better):
import Tkinter as tk
root=tk.Tk()
f=tk.Frame(root,width=100,height=300)
f.grid_propagate(False)
f.grid(row=0,column=0)
def resize(evt):
f.update_idletasks()
height=f.winfo_reqheight()
f.grid_propagate(False)
if(evt.width!=100) or (evt.height!=height):
f.configure(width=100,height=height)
print "HERE", evt.width,height
f.bind('<Configure>',resize)
#Just some stupid (ugly) code to update the size of the widget at runtime.
def add_label():
f.grid_propagate(True)
lbl=tk.Label(f,text=' %d Hello!'%(add_label.row))
lbl.grid(column=0,row=add_label.row)
add_label.row+=1
add_label.row=0
b=tk.Button(root,text="Add label",command=add_label)
b.grid(row=1,column=0)
root.mainloop()

Categories