grid layout just not aligning perfectly, even when following lecturer's code - python

I'm learning python doing a udemy course so its watching pre-recorded videos.
Following on recording laying out widgets on a screen (using tKinter) and just cannot get one button to align - even though I literally copy the code the lecturer has in the recording.
I am sure I am missing something really simple here. Everything lines up except the "Generate Password" button which is off to the right.
Its just a basic intro right now to GUI in Tkinter - so haven't gone into Frames as yet - just basic layouts.
Code is here - Everything aligns beautifully except the "Generate Password" button which shows up too far to the right - almost as if it is in another column...
from tkinter import *
def generate_pwd():
pass
def add_pwd():
pass
window = Tk()
window.title("Password Manager")
window.config(padx=50, pady=50)
# background canvas
canvas = Canvas(width=200, height=200)
img_lock = PhotoImage(file="logo.png")
canvas.create_image(100, 100, image=img_lock)
canvas.grid(column=1, row=0)
# widgets
lbl_website = Label(text="Website:")
lbl_website.grid(column=0, row=1, sticky=W)
lbl_email = Label(text="Email/UserName:")
lbl_email.grid(column=0, row=2, sticky=W)
lbl_password = Label(text="Password:")
lbl_password.grid(column=0, row=3, sticky=W)
ent_website = Entry(width=42)
ent_website.grid(column=1, row=1, columnspan=2, sticky=W)
ent_email = Entry(width=42)
ent_email.grid(column=1, row=2, columnspan=2, sticky=W)
ent_password = Entry(width=21)
ent_password.grid(column=1, row=3, sticky=W)
btn_generate = Button(text="Generate Password", command=generate_pwd)
btn_generate.grid(column=2, row=3)
btn_add = Button(text="ADD", width=35, command=add_pwd)
btn_add.grid(column=1, row=4, columnspan=2, sticky=W)
window.mainloop()
What I get:

From what I read, the width of a column is by default the width of its widest element; in your case, the culprit is the logo with its width of 100 that makes column 1 wider than it should be.
To fix this, just add a columnspan of 2 in your logo placement (in addition, the logo will be nicely centered):
...
canvas.create_image(100, 100, image=img_lock)
canvas.grid(column=1, row=0, columnspan=2)
...

Related

How to make tk.Button() fill width of window while using .grid()?

I've made a program that makes a pie chart using matplotlib.pyplot and it has a GUI which I made using tkinter, and I'm having problems with buttons. They don't fit the width of the window! This is the first time I'm using tkinter, so I don't know how to do that.
Here's the screenshot
Here's the snippet of my code which packs the buttons into the window:
append_btn = tk.Button(
text="Add values to register",
command=append
)
make_chart_btn = tk.Button(
text="Make chart",
command=make_chart
)
clear_btn = tk.Button(
text="Clear all registered values",
command=clear
)
exit_btn = tk.Button(
text="Exit Pie Chart Creator",
command=_exit_
)
append_btn.grid(row=4, column=1, sticky="E")
make_chart_btn.grid(row=4, column=2, sticky="W")
clear_btn.grid(row=5, column=1, sticky="E")
exit_btn.grid(row=5, column=2, sticky="W")
The code you have provided doesn't work. Please strip the unused functions out to make it work.
Nevertheless, the :
sticky="WENS"
flag is what you are looking for probably.
Sticky flag defines how to expand the widget if the resulting cell is larger than the widget itself. In your case you want to fill the cell thus expand it in all directions.
Thus the resulting window might look like this:
import tkinter as tk
append_btn = tk.Button(
text="Add values to register",
)
make_chart_btn = tk.Button(
text="Make chart",
)
clear_btn = tk.Button(
text="Clear all registered values",
)
exit_btn = tk.Button(
text="Exit Pie Chart Creator",
)
fill="WENS"
append_btn.grid(row=4, column=1, sticky=fill)
make_chart_btn.grid(row=4, column=2, sticky=fill)
clear_btn.grid(row=5, column=1, sticky=fill)
exit_btn.grid(row=5, column=2, sticky=fill)
tk.mainloop()
You have to add the width and height option in the button’s constructor:
button = tk.Button(root, text, width=your_size, height=your_size, command)

How can i attach my scroll bar to my text box on tkinter?

Im trying to use a vertical scrollbar for my text box but am coming across some problems:
I cant get the scroll bar to be directly touching the right side of the text box (so they are connected)
It seems the scroll bar wont affect my text box
I looked through some solutions but none seemed to work.
Heres my code:
from tkinter import *
writtenQ = Tk()
writtenQ.title("Written Response Question")
writtenQ.resizable(0,0)
header = LabelFrame(writtenQ, bg="white")
content = LabelFrame(writtenQ, bg="white")
header.columnconfigure(0, weight=1) # Forces column to expand to fill all available space
homeButton=Button(content,width=50,height=50)
try:
homeIcon=PhotoImage(file="yes.png")
homeButton.config(image=homeIcon)
homeButton.image = homeIcon
except TclError:
print("Home")
homeButton.grid(row=1, sticky="w", padx=15, pady=2)
#the image of the question will be put here
titleHeader = Label(content, text="Question Image here",pady=15, padx=20, bg="white", font=("Ariel",20, "bold"), anchor="w", relief="solid", borderwidth=1)
titleHeader.grid(row=2, column=0, columnspan=3, padx=15, pady=5, ipadx=370, ipady=150)
#this will allow the user to input their written response
answerInput = Text(content, width = 60, borderwidth=5, font=("HelvLight", 18))
answerInput.grid(row=3, column=0, ipady = 10, sticky="w", padx=(20,0), pady=20)
answerScrollBar= Scrollbar(content, command=answerInput.yview, orient="vertical")
answerScrollBar.grid(row=3, column=1, sticky="w")
submitButton = Button(content, borderwidth=1, font=("Ariel", 22), text="Submit", bg="#12a8e3", fg="black", activebackground="#12a8e3", relief="solid")
submitButton.grid(row=3, column=2, ipady=50, ipadx=70, sticky="nw", pady=20)
header.grid(row=0, sticky='NSEW')
content.grid(row=1, sticky='NSEW')
Configuring a scrollbar requires a two-way connection: the scrollbar needs to call the yview or xview method of the widget, and the widget needs to call the set method of the scrollbar.
Usually, this is done in three steps like in the following example:
answerInput = Text(...)
answerScrollBar= Scrollbar(..., command=answerInput.yview)
answerInput.configure(yscrollcommand=answerScrollBar.set)
You are forgetting the final step.
Unrelated to an actual functioning scrollbar, you're going to want to be able to see the scrollbar. You need to use sticky="ns" for the scrollbar so that it stretches in the Y direction. Otherwise it will only be a couple dozen pixels tall.
answerScrollBar.grid(row=3, column=1, sticky="ns")
Have you tried the solution here?
Let's say that the text widget is called text. Your code could be (excluding the setup of the window):
import tkinter
import tkinter.ttk as ttk
scrollb = ttk.Scrollbar(self, command=text.yview)
scrollb.grid(row=0, column=1, sticky='nsew')
text['yscrollcommand'] = scrollb.set
I have picked out what I think will be ueful for you from Honest Abe's answer. Hope it helped. Remember to set up your window before using the code...

Tkinter Python Remove Vertical Spaces

import tkinter as tk
root = tk.Tk()
buttonOK = tk.Button(root, text='B1')
MCC = tk.Button(root, text='B2')
TID = tk.Button(root, text='B3')
CURRENCY = tk.Button(root, text='B4')
COUNTRY = tk.Button(root, text='B5')
RESPONSE = tk.Button(root, text='B6')
B1.grid(row=3, column=0, sticky=tk.E+tk.W)
B2.grid(row=3, column=1, sticky=tk.E+tk.W)
B3.grid(row=3, column=2, sticky=tk.E+tk.W)
B4.grid(row=4, column=0, sticky=tk.E+tk.W)
B5.grid(row=4, column=1, sticky=tk.E+tk.W)
B6.grid(row=4, column=2, sticky=tk.E+tk.W)
label1 = tk.Entry(root, bd =8)
label1.grid(row=2, column=0, rowspan=1, columnspan=3, sticky=tk.E+tk.W)
label=tk.Text(root,background="yellow")
label.insert(index=0.0, chars="Enter values below\nand click search.\n")
label.grid(row=0, column=0,rowspan=1, columnspan=3, sticky=tk.E+tk.W)
root.mainloop()
I am trying to build a GUI in Python using Tkinter but the space for the inserted text label as "Enter values below\nand click search.\n" occupies about 6 blank rows. Please help me remove it. My current result using the code above is the left one, I want to have the right one image.
When you create the text widget, specify the number of lines you want it to display, for example:
label=tk.Text(root,background="yellow", height=3)
Failing to specify means it will default to 24, hence why it is so large in your program.
Ignoring the grid() mistake in your code.
You could correct the sizing issue by providing a weight and starting geometry size.
UPDATE:
If you provide weights to the proper rows and columns give your Text widget a default height of say 3 and tell the grid() on your Text widget to sticky="nsew" you can have your program start out the size you want and be able to resize evenly if you want to.
Take a look at the below code:
import tkinter as tk
root = tk.Tk()
# we want all 3 columns to resize evenly for the buttons so we provide
# a weight of 1 to each. We also want the first row where the text box is
# to resize so there is not unwanted behavior when resizing, so we set its weight to 1.
# keep in mind a weight of zero (default) will tell tkinter to not resize that row or column.
root.columnconfigure(0, weight=1)
root.columnconfigure(1, weight=1)
root.columnconfigure(2, weight=1)
root.rowconfigure(0, weight=1)
buttonOK = tk.Button(root, text='B1')
MCC = tk.Button(root, text='B2')
TID = tk.Button(root, text='B3')
CURRENCY = tk.Button(root, text='B4')
COUNTRY = tk.Button(root, text='B5')
RESPONSE = tk.Button(root, text='B6')
# corrected the variables being assigned a grid location
buttonOK.grid(row=3, column=0, sticky=tk.E+tk.W)
MCC.grid(row=3, column=1, sticky=tk.E+tk.W)
TID.grid(row=3, column=2, sticky=tk.E+tk.W)
CURRENCY.grid(row=4, column=0, sticky=tk.E+tk.W)
COUNTRY.grid(row=4, column=1, sticky=tk.E+tk.W)
RESPONSE.grid(row=4, column=2, sticky=tk.E+tk.W)
label1 = tk.Entry(root, bd =8)
label1.grid(row=2, column=0, rowspan=1, columnspan=3, sticky=tk.E+tk.W)
# added a height of 3 to the Text widget. to reduce its starting height
label=tk.Text(root,background="yellow", height=3)
label.insert(index=0.0, chars="Enter values below\nand click search.\n")
# added stick="nsew" so the text box will resize with the available space in the window.
label.grid(row=0, column=0,rowspan=1, columnspan=3, sticky="nsew")
root.mainloop()

Is it possible to make 'dynamically' adjustable widgets in Tkinter/ttk

I'm developing very simple GUI for my DB. It shows record's list/tree in DB on left panel and (if user clicks on some record) shows the record on the right panel.
Here some bit of code which creates GUI
from Tkinter import *
import ttk
master = Tk()
reclist = ttk.Treeview(columns=["TIME STAMP","HASH","MESSAGE"])
ysb = ttk.Scrollbar(orient=VERTICAL, command= reclist.yview)
xsb = ttk.Scrollbar(orient=HORIZONTAL, command= reclist.xview)
reclist['yscroll'] = ysb.set
reclist['xscroll'] = xsb.set
reclist.grid(in_=master, row=0, column=0, sticky=NSEW)
ysb.grid(in_=master, row=0, column=1, sticky=NS)
xsb.grid(in_=master, row=1, column=0, sticky=EW)
Comment = Text(master)
Comment.tag_configure("center", justify='center')
ysc = ttk.Scrollbar(orient=VERTICAL, command= Comment.yview)
xsc = ttk.Scrollbar(orient=HORIZONTAL, command= Comment.xview)
Comment.grid(in_=master,row=0,column=2,sticky=W+E+N+S)#, columnspan=5)
ysc.grid(in_=master, row=0, column=3, sticky=NS)
xsc.grid(in_=master, row=1, column=2, sticky=EW)
master.rowconfigure(0, weight=3)
master.columnconfigure(0, weight=3)
master.columnconfigure(2, weight=3)
master.mainloop()
Everything works pretty well, except that two panels are not adjustable. I cannot move border between them to make list of records or record panel bigger or smaller. I'm pretty sure in is possible (for example in gitk you can move the border between the list of commits and a displaied commit). I've search quite a lot with no luck.
What you are looking for is called a "PanedWindow". Both the tkinter and ttk modules have one, and they work almost identically. The general idea is that you create a PanedWindow instance, and then you add two or more widgets to it. The PanedWindow will add a movable slider between each widget. Typically you would use frames, which you can then fill up with other widgets.
Here is an example using the one in Tkinter:
import Tkinter as tk
root = tk.Tk()
pw = tk.PanedWindow()
pw.pack(fill="both", expand=True)
f1 = tk.Frame(width=200, height=200, background="bisque")
f2 = tk.Frame(width=200, height=200, background="pink")
pw.add(f1)
pw.add(f2)
# adding some widgets to the left...
text = tk.Text(f1, height=20, width=20, wrap="none")
ysb = tk.Scrollbar(f1, orient="vertical", command=text.yview)
xsb = tk.Scrollbar(f1, orient="horizontal", command=text.xview)
text.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set)
f1.grid_rowconfigure(0, weight=1)
f1.grid_columnconfigure(0, weight=1)
xsb.grid(row=1, column=0, sticky="ew")
ysb.grid(row=0, column=1, sticky="ns")
text.grid(row=0, column=0, sticky="nsew")
# and to the right...
b1 = tk.Button(f2, text="Click me!")
s1 = tk.Scale(f2, from_=1, to=20, orient="horizontal")
b1.pack(side="top", fill="x")
s1.pack(side="top", fill="x")
root.mainloop()

Python & ttk Using labelFrames to clean up a frame

I'm trying to build a basic GUI using ttk / Tkinter.
I have a plotted out a basic GUI that has the right basic components, but when I try and prettify it / space it out, I'm reach my limit of getting ttk containers to play nicely...
Examples:
from Tkinter import *
import ttk
class MakeGUI(object):
def __init__(self,root):
self.root = root
self.root.title("Text Comparitor")
## build frame
self.mainframe = ttk.Frame(self.root, padding="3 3 12 12")
self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
self.mainframe.columnconfigure(0, weight=1)
self.mainframe.rowconfigure(0, weight=1)
self.mainframe.pack()
## text labels
ttk.Label(self.mainframe, text="Conversion Truth Tester", font=("Helvetica", 32)).grid(column=1, row=1, sticky=E)
self.mainframe.pack(side="bottom", fill=BOTH, expand=True)
self.mainframe.grid()
ttk.Label(self.mainframe, text="Source Filename:").grid(column=1, row=2, sticky=W)
ttk.Label(self.mainframe, text="Source Text:").grid(column=1, row=3, sticky=W)
ttk.Label(self.mainframe, text="Converted Text:").grid(column=1, row=4, sticky=W)
ttk.Label(self.mainframe, text="Cleaned Source:").grid(column=1, row=5, sticky=W)
ttk.Label(self.mainframe, text="Cleaned Converted:").grid(column=1, row=6, sticky=W)
ttk.Label(self.mainframe, text="Details:").grid(column=1, row=7, sticky=W)
## buttons
self.close = ttk.Button(self.mainframe, text="Close",command=self.closeFrame).grid(column=1, row=9, sticky=SE)
self.next = ttk.Button(self.mainframe, text="Next",command=self.nextPara).grid(column=1, row=9, sticky=S)
self.next = ttk.Button(self.mainframe, text="Prev",command=self.prevPara).grid(column=1, row=9, sticky=SW)
def closeFrame(self):
self.root.destroy()
def nextPara(self):
pass
def prevPara(self):
pass
def main():
root = Tk()
makeGUI = MakeGUI(root)
root.mainloop()
if __name__ == '__main__':
main()
Which results in:
I've been trying to add a 2nd container object, a label frame to hold the text label objects, which results in the buttons moving further up (and so I assume I'm not referencing the labelframe into the grid properly:
from Tkinter import *
import ttk
class MakeGUI(object):
def __init__(self,root):
self.root = root
self.root.title("Text Comparitor")
## build frame
self.mainframe = ttk.Frame(self.root, padding="3 3 12 12")
self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
self.mainframe.columnconfigure(0, weight=1)
self.mainframe.rowconfigure(0, weight=1)
self.mainframe.pack()
## text labels
ttk.Label(self.mainframe, text="Conversion Truth Tester", font=("Helvetica", 32)).grid(column=1, row=1, sticky=E)
self.lfdata = ttk.Labelframe(self.root, labelwidget=self.mainframe, text='Label')#
self.lfdata.grid()
ttk.Label(self.lfdata, text="Source Filename:").grid(column=1, row=2, sticky=W)
ttk.Label(self.lfdata, text="Source Text:").grid(column=1, row=3, sticky=W)
ttk.Label(self.lfdata, text="Converted Text:").grid(column=1, row=4, sticky=W)
ttk.Label(self.lfdata, text="Cleaned Source:").grid(column=1, row=5, sticky=W)
ttk.Label(self.lfdata, text="Cleaned Converted:").grid(column=1, row=6, sticky=W)
ttk.Label(self.lfdata, text="Details:").grid(column=1, row=7, sticky=W)
## buttons
self.close = ttk.Button(self.mainframe, text="Close",command=self.closeFrame).grid(column=1, row=9, sticky=SE)
self.next = ttk.Button(self.mainframe, text="Next",command=self.nextPara).grid(column=1, row=9, sticky=S)
self.next = ttk.Button(self.mainframe, text="Prev",command=self.prevPara).grid(column=1, row=9, sticky=SW)
def closeFrame(self):
self.root.destroy()
def nextPara(self):
pass
def prevPara(self):
pass
def main():
root = Tk()
makeGUI = MakeGUI(root)
root.mainloop()
if __name__ == '__main__':
main()
Which results in: Note the swap of positions between buttons abd labels, and the just about visible aspects of the labelframe.
I'm trying to get the 2nd version to 'look' like a prettier version of the 1st.
Any pointers - I've been reading around the various resources / docs, and can't find anything that fits my example (most likely - I'm doing something silly...) and nothing I've tried has worked yet - including pack(), grid() and other snippets I've found in other related examples.
There are many places that require adjustments, let us comment on them (I will probably forget about something, so be sure to check the code at bottom).
First of all, applying weights to columns/rows in the frame alone is not going to make it expand as you resize the window. You need to do it in root. After that you might want to do it in the frame, to match your expectations about the layout after a resize. In your case, what makes most sense is making every column have the same weight > 0 and only making the second row have weight > 0. The reasoning for the columns is that you have 3 buttons, and you will want them all to expand in the free space in the same way. For the second part, that is a direct observation considering that you have a Labelframe at the second row. Giving a weight > 0 for any other row is going to give you a very weird layout. Weighting issues done.
Next thing I observed was your top label with a larger font. You certainly want it to span 3 columns (again, this number 3 is related to the row of buttons you will create at a later time). You may also want the text to be centered in these 3 columns (I'm not sure about your preferences here).
Now the Labelframe you create. It is just wrong, the labelwidget option does not mean what you think it does. It specifies a Label widget to serve as the label for this label frame. Thus, specifying your main frame for this parameter makes no sense. Maybe you want to specify some text to be visible at a certain position in the label frame. Also, this label frame must be grided with a columnspan of 3 too.
For the "gridding" in general I recommend specifying the option in_, so you make clear in relation to what widget you are "gridding". With that, it becomes obvious to start at column=0, row=0 each time you deepen your widget parenting level.
Here is how I adjusted your code:
import Tkinter
import ttk
class MakeGUI(object):
def __init__(self,root):
self.root = root
self.root.title(u"Title")
## build frame
self.mainframe = ttk.Frame(self.root, padding=(6, 6, 12, 12))
self.mainframe.grid(sticky='nwse')
for column in range(3):
self.mainframe.columnconfigure(column, weight=1)
self.mainframe.rowconfigure(1, weight=1)
## text labels
ttk.Label(self.mainframe, text=u"Label Title", anchor='center',
font=("Helvetica", 32)).grid(in_=self.mainframe,
column=0, row=0, columnspan=3, sticky="ew")
self.lfdata = ttk.Labelframe(self.mainframe, padding=(6, 6, 12, 12),
text='Labelframe')
self.lfdata.grid(column=0, columnspan=3, row=1, sticky='nsew')
info = (u"Source Filename", u"Source Text", u"Converted Text",
u"Cleaned Source", u"Cleaned Converted", u"Details")
for i, item in enumerate(info):
ttk.Label(self.lfdata, text=u"%s:" % item).grid(in_=self.lfdata,
column=0, row=i, sticky='w')
## buttons
btn = (u"Close", u"Next", u"Prev")
for i, item in enumerate(btn):
ttk.Button(self.mainframe, text=item).grid(in_=self.mainframe,
column=i, row=3)
def main():
root = Tkinter.Tk()
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
makeGUI = MakeGUI(root)
root.mainloop()
if __name__ == '__main__':
main()
Here is how it looks when the program starts and after some resizing:

Categories