Python 3: Positions of button and input text in a text box - python-3.x

I am new to python and learning to create a text box with two entries and one button.
I am able to set the position of my both entries, but I am not able to set the position of my button below them.
I tried to use:
b.place(anchor=S, bordermode=OUTSIDE, height=5, width=10)
but the button doesn't move at all. It stays at the lower right corner.
Following is my code:
from tkinter import *
root = Tk()
l1 = Label(root, text="Variable_1")
l1.pack( side = LEFT)
e1 = Entry(root, bd = 5)
e1.pack(side = LEFT)
l2 = Label(root, text="Variable_2")
l2.pack( side = LEFT)
e2 = Entry(root, bd = 5)
e2.pack(side = LEFT)
l = Label(root)
def callback():
x = e1.get()
y = e2.get()
print(x)
print(y)
b = Button(root, text="OK", command=callback)
for widget in (e1, e2, l, b):
widget.pack()
How can I place the button at the bottom-centre of the text box?
Also, any suggestions to change the positions of the entries?
Thanks!

The usual way to build complex layouts is to group associated widgets together with frames. In the example below I'm grouping the entrys in one frame and the button in another. This makes it easier to control the vertical positioning.
from tkinter import *
root = Tk()
top = Frame(root) # Create frame to hold entrys
top.pack() # Pack top frame
l1 = Label(top, text="Variable_1")
l1.pack(side=LEFT)
e1 = Entry(top, bd=5)
e1.pack(side=LEFT)
l2 = Label(top, text="Variable_2")
l2.pack(side=LEFT)
e2 = Entry(top, bd=5)
e2.pack(side=LEFT)
l = Label(root)
def callback():
x = e1.get()
y = e2.get()
print(x)
print(y)
bottom = Frame(root) # Create frame to hold button
bottom.pack() # Pack bottom frame
b = Button(bottom, text="OK", command=callback)
b.pack()
You can also use the grid() geometry manager. See The Tkinter Grid Geometry Manager

Related

Tkinter dialog's elements position

I am building custom Tkinter dialog window with Entry and Combobox. I am stuck with placing text and enter frames. Currently I am placing them manually. I am looking for the way to let tkinter do it automatically (maybe with pack() method). And also configure TopLevel size automatically.
My code:
def ask_unit_len():
values = ['millimeters', 'micrometers', 'nanometers']
top = Toplevel()
top.geometry('170x100')
top.resizable(False, False)
top.focus_set()
top.grab_set()
top.title('Enter length and units')
label_length = Label(top, text='Length:')
label_length.place(x=0, y=0)
units_type = StringVar()
length = StringVar()
answer_entry = Entry(top, textvariable=length, width=10)
answer_entry.place(x=55, y=0)
label_units = Label(top, text='Units:')
label_units.place(x=0, y=30)
combo = Combobox(top, width=10, textvariable=units_type,
values=values)
combo.place(x=50, y=30)
button = Button(top, text='Enter',
command=lambda:
mb.showwarning("Warning",
"Enter all parameters correctly")
if (units_type.get() == "" or not length.get().isdigit()
or int(length.get()) <= 0)
else top.destroy())
button.place(x=65, y=70)
top.wait_window(top)
return int(length.get()), units_type.get()
So, is there any way to perform this?

Using grid how do I not have frames push each other out of place

Been working on this problem for more than a day.
I would like for my top frames to be right up against the picture.
When I don't include section 2 frames below the northern frame it works out nicely.
I have looked up and tried using column_configure, tried weight.
Honestly I am probably using them wrong.
I know there are fixes, here, but its not yet clicking in my mind on how to make it work.
If possible could you not use CLASSES or to many fancy FUNCTIONS.
Honestly at this stage I am weak in them at the moment.
Thanks in advance for taking the time to read and for any assistance offered.
from tkinter import *
import os
import PIL.Image
import random
import sys
import tkinter as tk
global logo
global po
# path to image file
home = os.path.expanduser('~')
dt = desktop_top = os.path.join(home,'Desktop')
p3p = python_3_projects = os.path.join(dt,'Python 3 Projects')
ilfru = images_labeled_for_reuse = os.path.join(p3p,'Images Labeled for Reuse')
gp = gurps_path = os.path.join(ilfru,'GURPS')
gg = gurps_gif = os.path.join(gp,'GIF')
fc = 1
# Count number of file in GURPS/GIF folder
for root, dirs, files in os.walk(gg):
for filenames in files:
fc +=1
# Pick a random number based off number of files
rn = random.randint(1,fc)
# Cycle through files and assign the random file to logo
fc = 1
# Count number of file in GURPS/GIF folder
for root, dirs, files in os.walk(gg):
for filenames in files:
if fc == rn:
logo = os.path.join(gg,filenames)
fc +=1
# Get Image Size
# po PIL Open
po = open(logo,"rb")
img = PIL.Image.open(po)
# Image Width, Image Height
iw,ih = img.size
ih = ih + 5
root = tk.Tk()
# Round Down Nearest tenth
def rd(num,divisor):
x = (num/divisor)-(num%divisor)
x = int(x)
return x
# Get Screen Dimensions
sh = screen_height = root.winfo_screenheight()
sw = screen_width = root.winfo_screenwidth()
# Window size
rg = str(sw)+'x'+str(sh)
root.geometry(rg)
# Create all frames
nwfh = rd(sh, 10)
nwfw = rd(sw, 4)
equal_sides = nwfh + nwfw
equal_sides = rd(equal_sides,4)
ntf = rd(sw-equal_sides,1)
# Frames
spacer_width = rd(equal_sides,10)
frame0 = Frame(root,bg='black',width=sw,height=sh,padx=5,pady=5)
frame0.grid(row=0,column=0)
north_west_top_frame = Frame(frame0, bg='cyan', width=equal_sides, height=equal_sides)
frame_spacer0 = Frame(frame0, bg='beige', width=spacer_width, height=ih)
northern_top_frame = Frame(frame0,bg='alice blue', width=ntf,height=ih,padx=15,pady=15)
# Layout all frames
# Trying configure
# northern_top_frame.grid_columnconfigure(1,weight=1)
# Frame Section 1
north_west_top_frame.grid(row=0,column=0, sticky="nw")
frame_spacer0.grid(row=0, column=1, sticky="w")
northern_top_frame.grid(row=0,column=2)
# Put logo in nw frame
glogo = tk.PhotoImage(file=logo)
gurps_logo = tk.Label(north_west_top_frame, compound = tk.CENTER, text='GURPS\nCHARACTER\nSHEET',fg = "red", font="Helvetica 10 bold", image=glogo).grid(row=0,column=0)
l1 = tk.Label(northern_top_frame, anchor='w', text="Name: ", font = "Helvetica 10 bold",width=17)
l1.grid(row=0, column=0)
e1 = Entry(northern_top_frame)
e1.grid(row=0,column=1)
# Spacer Width = pic prame width -
# ih is northern frame height
# need label height
spacer_height = ih-30
# spacer
# s1 = Frame(northern_top_frame,bg='firebrick', width=ntf,height=spacer_height)
# s1.grid(row=1,column=1, columnspan=11, sticky=tk.W+tk.E)
l4 = tk.Label(northern_top_frame, anchor='w', text="Ht: ", font = "Helvetica 10 bold", width=17)
l4.grid(row=2, column=0)
e4 = Entry(northern_top_frame)
e4.grid(row=2,column=1)
l9 = tk.Label(northern_top_frame, anchor='w', text="Appearance: ", font = "Helvetica 10 bold", width=17)
l9.grid(row=4, column=0)
e9 = Entry(northern_top_frame)
e9.grid(row=4,column=1, columnspan=11, sticky=tk.W+tk.E)
# Section 2
s2 = Frame(frame0,bg='firebrick', width=ntf)
s2.grid(row=5,column=0, sticky="w")
l10 = tk.Label(s2, text="LEVEL")
l10.grid(row=6,column=1)
root.mainloop()
If I understand correctly, your problem basically boils down to this: the beige and white frames are pushed to the right by the red frame, although there is enough room right next to the cyan frame
The Minimal Complete Verifiable Example for this could be as simple as:
from tkinter import *
root = Tk()
frame0 = Frame(root, bg='black', width=500, height=300, padx=5, pady=5)
frame0.grid(row=0, column=0)
north_west_top_frame = Frame(frame0, bg='cyan', width=150, height=150)
frame_spacer0 = Frame(frame0, bg='beige', width=50, height=150)
northern_top_frame = Frame(frame0, bg='alice blue', width=300, height=150)
north_west_top_frame.grid(row=0, column=0, sticky="nw")
frame_spacer0.grid(row=0, column=1, sticky="w")
northern_top_frame.grid(row=0, column=2)
s2 = Frame(frame0, bg='firebrick', width=500, height=150)
s2.grid(row=5, column=0, sticky="w")
root.mainloop()
The problem here is how you define the grid. You put both north_west_top_frame and s2 in column 0, the leftmost column. The you put frame_spacer0 one column to the right and northern_top_frame one column further than that. Every column stretches to fit everything you put in it, so the first column (column 0) gets as wide as s2. The next column, with the beige frame, is next to the first column, so it will start where the red frame ends.
What you seem to want is that the red frame is not only in column 0, but also in column 1 and 2, under the beige and white frames. To do that use columnspan:
s2.grid(row=5, column=0, columnspan=3, sticky="w")
When you do this you see the red frame spans all the way across the cyan, beige and white frames:

Tkinter, scroll other text box to that text contained in text box 1 on click. Is it possible without tag bind?

DESCRIPTION:
I created 3 textboxes. First and second are populated it with text.
The third is the comparison between the first 2 textboxes.
I used tag_configure to display the differences.
QUESTION:
I want that when I click on the sentence or any part of the text in textbox 1 or 2 or 3, it should scroll the other textboxes to that text.
How shall I extract the text on a click?
OUTPUT:
When I click 26 on text box 1, it should scroll tb 2 & 3 to that specific text (i.e.: 26).
CODE:
import diff_match_patch as dmp_module
import tkinter as tk
k1 = []
k2=[]
for i in range(100):
k1.append(" This is the " + str(i) + " document\n")
k2.append(" This is the " + str(i+5) + " doc\n")
diff = []
for i in range(len(k1)):
dmp = dmp_module.diff_match_patch()
diff.append(dmp.diff_main(k1[i], k2[i]))
(dmp.diff_cleanupSemantic(diff))
root = tk.Tk()
journal2 = tk.Text(root, borderwidth=2, highlightthickness=0, width = 45, height = 30)
journal2.insert("end","Textbox 1\n\n\n")
journal2.insert("end","\n".join(k1))
journal2.pack(side = 'left')
journal3 = tk.Text(root, borderwidth=2, highlightthickness=0, width = 45, height = 30)
journal3.insert("end","Textbox 2\n\n\n")
journal3.insert("end","\n".join(k2))
journal3.pack(side = 'left')
journal1 = tk.Text(root, borderwidth=2, highlightthickness=0, width = 45, height = 30)
journal1.insert("end","Textbox 3\n\n\n")
journal1.pack(side = 'left')
journal1.tag_config('insert', foreground="navy", font='Courier 10 bold')
journal1.tag_config('delete', foreground="red2", overstrike=True, )
def add_hyperlink2(section, tag2):
journal1.insert("end",section,('delete', tag2))
def add_hyperlink3(section, tag2):
journal1.insert("end",section,('insert', tag2))
def add_hyperlink4(section, tag2):
journal1.insert("end",section, tag2)
for y in range(len(diff)):
for q in range(len(diff[y])):
if diff[y][q][0] == -1:
add_hyperlink2(diff[y][q][1], diff[y][q][1])
elif diff[y][q][0] == 1:
add_hyperlink3(diff[y][q][1], diff[y][q][1])
else:
add_hyperlink4(diff[y][q][1], diff[y][q][1])
root.mainloop()
How shall I extract the text on a click?
If you bind to the mouse button click, the event object that is passed to the callback has the x/y coordinates of where the click occurred. You can use that to get the index of the line that was clicked on. If you bind to the release of the button, then the text widget will have updated the insertion cursor and you can use that to know what line was clicked on.
Here's a simple example:
import tkinter as tk
def on_click(event):
index = text.index("#%s,%s" % (event.x, event.y))
line, char = index.split(".")
label1.configure(text="Click on line %s" % line)
def on_click_release(event):
index = text.index("insert")
line, char = index.split(".")
label2.configure(text="Click Release on line %s" % line)
root = tk.Tk()
label1 = tk.Label(root, anchor="w")
label2 = tk.Label(root, anchor="w")
text = tk.Text(root)
label1.pack(side="top", fill="x")
label2.pack(side="top", fill="x")
text.pack(side="bottom", fill="both", expand=True)
for i in range(1, 20):
text.insert("end", "line #%s\n" % i)
text.bind("<ButtonPress-1>", on_click)
text.bind("<ButtonRelease-1>", on_click_release)
root.mainloop()
When you run the above code, if you click and hold the mouse button over a line, the line number will appear at the top using the x/y coordinates. When you release the button, the line number will appear using the insert index.

bordermode attribute in place() method of tkinter python

What is the difference between bordermode = OUTSIDE and bordermode = INSIDE attribute in place() method of tkinter in python?
From the tkinter/__init__.py file:
bordermode="inside" or "outside" - whether to take border width of
master widget into account
For example:
from tkinter import *
root = Tk()
f1 = Frame(root, borderwidth=5, relief=SUNKEN, width=50, height=50)
f1.pack()
l1 = Label(f1, text="Hi")
l1.place(x=10, y=10, bordermode="outside")
f2 = Frame(root, borderwidth=5, relief=SUNKEN, width=50, height=50)
f2.pack()
l2 = Label(f2, text="Hi")
l2.place(x=10, y=10, bordermode="inside")
root.mainloop()
So, outside counts x and y from the top left corner of the frame including the border, while inside counts it without the border.

Trouble with Tkinter Scrollbar

I am trying to attach a scrollbar to my listbox in one of my toplevels. However, instead of attaching to my listbox it is attaching itself to the toplevel instead. I am at a complete lost here. Any ideas on what I am doing wrong? I can provide the whole program code if anyone needs it.
def onNewRecipe(self):
self.top = Toplevel()
self.top.title("New Recipe")
quantity = StringVar()
#Center the window
w = 600
h = 600
sw = self.top.winfo_screenwidth()
sh = self.top.winfo_screenheight()
x = (sw - w)/2
y = (sh - h)/2
self.top.geometry('%dx%d+%d+%d' % (w, h, x, y))
#Add quantity label
addQuantity = Label(self.top, text="Add Quantity:")
addQuantity.place(x=0,y=0)
quantityAdd = Entry(self.top, textvariable=quantity)
quantityAdd.place(x=150, y=0)
#Add ingredient label
addIngredient = Label(self.top, text="Add Ingredients:")
addIngredient.place(x=0,y=30)
ingredientAdd = Entry(self.top)
ingredientAdd.place(x=150, y=30)
#Select measurements label
selectMeasurement = Label(self.top, text="Select Measurements:")
selectMeasurement.place(x=0, y=60)
measurement = StringVar()
measurement.set("ounce")
measurementSelect = OptionMenu(self.top, measurement, "ounce", "pound", "gallon", "quart", "fl oz", "pint", "cup", "table spoon", "teaspoon")
measurementSelect.place(x=150, y=60)
#Add measurements label
addMeasurement = Label(self.top, text="Amount:")
addMeasurement.place(x=0, y=100)
measurementAdd = Entry(self.top)
measurementAdd.place(x=150, y=100)
#Add the textwidget
recipeText = Text(self.top)
recipeText.place(x=0,y=200)
#Cooking direction label
cookingDirection = Label(self.top, text="Cooking Direction")
cookingDirection.place(x=0,y=175)
def onNewIngredient():
qVar = quantity.get()
print(qVar)
#Add the Add Button
addButton = Button(self.top, text="Add", command= onNewIngredient)
addButton.place(x=0, y=130)
#Add Ingredients listbox
ingredScroll = Scrollbar(self.top, orient=VERTICAL)
ingredientListbox = Listbox(self.top, yscrollcommand=ingredScroll.set)
ingredScroll.config(command=ingredientListbox.yview)
ingredScroll.pack(side=RIGHT, fill=Y)
ingredientListbox.place(x=450, y=0)
Browsing this tutorial, it looks like the usual way to do this is to create a Frame that contains exactly two widgets: the list box and the scroll bar. In your case, it would look like:
#Add Ingredients listbox
box = Frame(self.top)
ingredScroll = Scrollbar(box, orient=VERTICAL)
ingredientListbox = Listbox(box, yscrollcommand=ingredScroll.set)
ingredScroll.config(command=ingredientListbox.yview)
ingredScroll.pack(side=RIGHT, fill=Y)
ingredientListbox.pack()
box.place(x=450, y=0)

Resources