Can't position Entry with grid? - python

Below Entry and button are not placing themselves according to the grid parameters. What can cause this?
class tt(Frame):
def __init__(self, master):
super(tt, self).__init__(master)
self.grid()
self.widget()
def widget(self):
self.inputType = Entry(self)
self.inputType.grid(row = 3, column = 3)
root = Tk()
root.title("Test")
root.geometry("300x300")
app = tt(root)
root.mainloop()

The behavior you see is correct. Tkinter is working as designed.
Columns and rows have a width and height of zero unless there is something in them. So even though you put something in column 3, it will appear at the left margin unless there's something in the other columns. Likewise, if there's nothing in rows 0, 1 or 2, something in row 3 will appear at the top of its parent.

Related

Can't move object with grid

I've researched this issue, and found people with similar problem. However, the answers are not clear for me.
I'm trying to freely move my objects into X rows and columns. Right now, regardless of what value I put into column or row my object won't change position, it's always on the top-left corner.
According to other people's answers, empty rows and columns have zero size. How can I change that?
This is my current code:
from tkinter import *
class Application:
def __init__(self, master):
frame = Frame(master)
b = Button(text="This is a button")
b.grid(row=2, column=3)
root = Tk()
root.geometry('{}x{}'.format(500, 300))
app = Application(root)
root.mainloop()
Yes, empty rows and columns have zero size. So, without the help of any other options, your widget will be placed at the corner of the window. But with the help of options like padx, pady, sticky and functions like grid_columnconfigure() and grid_rowconfigure(), you can achieve what you want. As an example, look at this:
from tkinter import *
class Application:
def __init__(self, master):
frame = Frame(master)
b = Button(text="This is a button")
b.grid(row=0, column=0, pady=(20,0))
master.grid_columnconfigure(0, weight=1)
root = Tk()
root.geometry('{}x{}'.format(500, 300))
app = Application(root)
root.mainloop()

Grid and placing widgets : once again

Dears,
Although I want to make it simple, I'm still failing in creating a class of "windows" which by default will have a basic menu (not in code yet), a frame of 10 rows and 10 cls, and in the last cell of this frame (row = 9, col=9) a "Close" button.
Then, I could create several classes that will inherit from that one, and adding more widgets, commands, .. Well very basic
Yes but, although I gave weight to cells,..,... the button is still on the top left corner, and not the bottom right one. What did I miss when managing widgets with .grid()
Thks a lot
import tkinter as tk
from tkinter import ttk
class myWindows(tk.Tk):
def __init__(self,
pWtitle='',
pParent = '',
pIsOnTop = False,
pWidth=800,
pHeight=600,
pIsResizable=False,
pNbRows = 10,
pNbCols = 10):
tk.Tk.__init__(self)
tk.Tk.wm_title(self, pWtitle)
self.geometry('%sx%s' % (pWidth, pHeight))
if pIsResizable :
self.minsize(pWidth, pWidth)
rFrame =tk.Frame(self, borderwidth=1, relief="ridge")
#to make it simple by default, there will be a grid of 10rows and 10columns
for r in range(pNbRows) :
rFrame.grid_rowconfigure(r,weight=1)
for c in range(pNbCols) :
rFrame.grid_columnconfigure(c,weight=1)
rFrame.grid(row=0, column=0, sticky='ns')
#all windows will have a quit button on the bottom right corner (last cells)
#Need deduct 1 in the parameter as indexes start from 0
bt=ttk.Button(self, text='Close',command=self.quit)
bt.grid(row=pNbRows -1, column=pNbCols -1, sticky='se')
app = myWindows( pWtitle='MAIN')
app.mainloop()
You are configuring the weights inside of rFrame, but you are putting rFrame and the button directly in the root window. You have not configured weights for any rows or columns in the root window.
grid doesn't display rows and columns that don't contain anythng. Try, for example adding one placeholder label with empty picture (Label = (self, image = PhotoImage())) in every row and column of the grid until you populate it with real stuff. Source
http://effbot.org/tkinterbook/grid.htm
minsize= Defines the minimum size for the row. Note that if a row is
completely empty, it will not be displayed, even if this option is
set.
Finally, I came up with a solution :
import tkinter as tk
from tkinter import ttk
class myWindows(tk.Tk):
def __init__(self,
pWtitle='',
pParent = '',
pIsOnTop = False,
pWidth=800,
pHeight=600,
pIsResizable=False
):
tk.Tk.__init__(self)
tk.Tk.wm_title(self, pWtitle)
self.geometry('%sx%s' % (pWidth, pHeight))
if pIsResizable :
self.minsize(pWidth, pHeight)
#all windows will have a quit button on the bottom right corner (last cells)
#Need deduct 1 in the parameter as indexes start from 0
bt=ttk.Button(self, text='Close',command=self.quit)
bt.grid(row=1, column=0, sticky='se')
#to make it simple by default, there will be a container on top of button
rFrame =tk.Frame(self, borderwidth=1, bg = 'blue', relief="ridge")
rFrame.grid(row=0, column=0)
#give max weight to the first cell to
#make sure the container is filling up the empty space
#on top of the button(s)
self.grid_rowconfigure(0, weight = 1)
self.grid_rowconfigure(1, weight =0)
self.grid_columnconfigure(0, weight =1)
app = myWindows( pWtitle='MAIN')
app.mainloop()

Tkinter, doing multiple frames at once, with buttons in each frame. But the buttons ignore the frame they are given and just use the grid

I am writing a Tkinter program where I can make multiple frames that I can choose where they are relative to eachother, so I decided to use grid() to decide which frame does where. I also have a class AppButton that creates a button/label/entry. I am trying to pass in the frame I want the button to be created in, and use a grid to determine its position that is relative to the frame it is created in.
Currently however, the buttons don't get made in the frame I try to pass, but rather it uses the same grid as the frames do, and just gets put there, which looking back makes sense.
My question is, how can I change this so that the button's grid is a separate grid relative only to the frame it is created in? That way if I move the frame around, the buttons will too, but their relative position in the frame stays the same.
Right now the frame and buttons share the same grid and that is how their positions are determined.
import Tkinter as tk
import tkFileDialog as tkfd
class AppButton:
#simple button construction
#create a button with chosen arguments
def create_button(self, words, rownum, frame):
btn = tk.Button(frame, text = words)
btn.grid(row = rownum, column = 2)
def create_entry(self, rownum, frame):
txt = tk.Entry(frame)
txt.grid(row = rownum, column = 1)
def create_label(self, words, rownum, frame):
lbl = tk.Label(frame, text = words)
lbl.grid(row = rownum, column = 0)
#input is composed of a Label, an Entry, and a Button. calls the three funcs above
def create_input(self, words, rownum, frame):
self.create_label(words, rownum, frame)
self.create_entry(rownum, frame)
self.create_button("OK", rownum, frame)
class Application(tk.Frame):
"""A GUI application that creates multiple buttons"""
#create a class variable from the root(master) called by the constructor
def __init__(self, master):
self.master = master
master.title("The best GUI")
tk.Frame.__init__(self, master, width=200, height=200)
self.grid(row = 0, column = 0)
def new_frame(self, master, color, row, column):
frame = tk.Frame(master, width=200, height=200, bg=color)
frame.grid(row = row, column = column)
return frame
if __name__ == "__main__":
root = tk.Tk()
root.title("Button Test Launch")
app = Application(root)
appbutton = AppButton()
frame1 = app.new_frame(root, "red", 3, 1)
frame2 = app.new_frame(root, "blue", 2, 2)
appbutton.create_input("test1", 0, root)
appbutton.create_input("test2", 1, root)
appbutton.create_input("test3", 2, root)
appbutton.create_input("test4", 3, root)
root.mainloop()
Here is a picture of what it looks like now:
Current GUI
My desired end result is to have for example "test1 ______ [OK]" to be completely in a single of those frames.
You are telling the buttons to use root as their parent. Instead, you should be passing in the frame:
appbutton.create_input("test1", 0, frame1)
appbutton.create_input("test2", 1, frame1)
appbutton.create_input("test3", 2, frame2)
appbutton.create_input("test4", 3, frame2)

When using a scrolled canvas in Tkinter, columns of contained frame are cut off

I'm trying to make a Tkinter widget that contains a number of tables, which are currently frames with entries filled using the .grid method, which can be switched between by pressing buttons. My current attempt at a solution uses the following code:
from tkinter import *
def dot(root, num):
root.subframe.destroy()
root.subframe = TFrame(root, num)
root = Tk()
vscrollbar = Scrollbar(root,orient='vertical')
vscrollbar.grid(row=1,column=2,sticky=N+E+W+S)
root.defaultframe = MainFrame(root)
root.canvas = Canvas(root, yscrollcommand=vscrollbar.set)
root.subframe = Frame(root.canvas)
vscrollbar.config(command=root.canvas.yview)
root.canvas.grid(row=1,column=0)
root.subframe.grid(row=0,column=0)
where MainFrame has the following structure:
class MainFrame(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.grid(row=0,column=0)
b1 = Button(self, text='table 1', command=lambda: dot(root, 0))
b2 = Button(self, text='table 2', command=lambda: dot(root, 1))
b1.grid(row=0, column=0, sticky=N+E+W+S)
b2.grid(row=0, column=1, sticky=N+E+W+S)
and TFrame:
class TFrame(Frame):
def __init__(self, foor, num):
Frame.__init__(self, root.canvas)
for i in range(12):
self.grid_columnconfigure(i, minsize=50)
for x in range(12):
for y in range(20):
label = Label(self, text=num)
label.grid(row=y,column=x,sticky=N+E+W+S)
root.canvas.create_window((0,0),window=self,anchor='nw')
root.canvas.configure(scrollregion=root.canvas.bbox('all'))
When I run the code, pressing the buttons loads the tables, which scroll in the vertical as expected. But only the first 8 columns or so are visible, no matter how the window is resized. Changing the width of the MainFrame by adding empty labels and the like does not affect the size of the TFrame created, even if it is several times wider than the 8 columns the TFrame ends up being. While I could have a somewhat tolerable solution by adding a horizontal scroll bar as well as the vertical, my experiences so far with scrolling in tkinter in general have been negative enough that I hope to avoid using it by any possible means.
Okay, found a solution. It turns out there weren't columns being cut off, the whole canvas was being cut off, and all my test cases just happened to have exactly the right number of columns vs column width that it looked like the columns after the first 8 were being cut off.
Changing:
root.canvas.grid(row=1,column=0)
to
root.canvas.grid(row=1,column=0,sticky=N+E+W+S)
fixed the problem.

Labels appearing over Entry's in Tkinter

When I run the following code the created labels appear over top of the Entry boxes as if they are not being added to the same grid.
class Application(Frame):
def __init__(self,master):
super(Application,self).__init__(master)
self.grid()
self.new_intervals()
def new_intervals(self):
self.int_label = Label(text="Interval Name")
self.int_label.grid(row=0, column=0,sticky=W)
self.int_time_label = Label(text="Time (HH:MM:SS)")
self.int_time_label.grid(row=0, column=1,sticky=W)
self.box1 = Entry(self)
self.box1.grid(row=1,column=0,sticky=W)
self.box2 = Entry(self)
self.box2.grid(row=1,column=1,sticky=W)
self.box3 = Entry(self)
self.box3.grid(row=2,column=0,sticky=W)
self.box4 = Entry(self)
self.box4.grid(row=2,column=1,sticky=W)
root = Tk()
root.title("Interval Timer")
root.geometry("400x500")
app=Application(root)
root.mainloop()
I know that i can add these boxes in a loop, however, I can't get it to work without the loop at the moment
The application frame is in row 0, column 0 of the main window. That is the default when you don't specify anything. Also as a default, they appear in the middle
This frame has four entry widgets spread across two rows, making the frame grow to fit around those entry widgets
The "Interval Name" label is also being placed in row 0, column 0 of the main window, because that's what you explicitly tell it to do, and because its parent is the main window.
The "Time" label is also in row 0 of the main window because, again, it's parent is the main window
both of these labels are appearing in the vertical center of the row because that is the default behavior which you haven't overridden, which is why they appear on top of the entry widgets.
So, because the labels and the application frame are in the same row of the main window, and because the labels default to being in the vertical center, they appear to be in the middle of the entry widgets.
I assume you intended for the labels to be children of the frame, so you need to specify "self" as the first parameter when creating them:
self.int_label = Label(self, text="Interval Name")
...
self.int_time_label = Label(self, text="Time (HH:MM:SS)")
I also recommend grouping all of your grid statements for a particular master window together, so it's easier to see the organization of your widgets. In my experience this makes the code easier to read and easier to maintain.
For example:
self.int_label = Label(...)
self.int_time_label = Label(...)
self.box1 = Entry(...)
...
self.int_label.grid(...)
self.int_time_label.grid(...)
self.box1.grid(...)
...

Categories