bordermode attribute in place() method of tkinter python - python-3.x

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.

Related

Make multiple tk.Toplevel windows embedded/unified in main tk window

So I'm trying to create a program which uses multiple tk.Toplevel windows. The problem with this is, that all windows show up seperated as their "own App", so when you alt tab, you switch between the toplevel windows.
The pseudocode would look something like this:
import tkinter as tk
top_levels = {}
def open_toplevel():
top_level = tk.Toplevel(root)
top_level.geometry("300x200+0+0")
top_levels.update({f"toplevel{len(top_levels.keys())}" : top_level})
root = tk.Tk()
button = tk.Button(root, command= open_toplevel)
button.place(x=0, y=0)
root.mainloop()
So my question, is: is there a way to unify them into "one window"?
If you want all of them to unify into one window then tk.Frame is a better widget to use instead of tk.Toplevel
The purpose of tk.Toplevel is to create a new temporary window, not an extra part of the window. But frames are a really good way to organise stuff.
This code below creates new frame every time you click the button. This is just a simple example. You can also use grid for widgets in a frame. I also put a border so you can see where the frames are located.
from tkinter import *
def open_frame():
frame = Frame(root, highlightbackground="black", highlightthickness=2)
lbl1 = Label(frame, text=f"Frame {len(frames) + 1} label 1")
lbl2 = Label(frame, text=f"Frame {len(frames) + 1} label 2")
lbl1.pack()
lbl2.pack()
frame.pack(padx=5, pady=5)
frames.append(frame)
root = Tk()
frames = []
btn = Button(root, text="Open Frame", command=open_frame)
btn.pack()
root.mainloop()
I hope this solution is helpful
EDIT
Use this code here to move the frames:
from tkinter import *
def open_frame():
global frame, frames
frame = Frame(root, highlightbackground="black", highlightthickness=2)
lbl1 = Label(frame, text=f"Frame {len(frames) + 1} label 1")
lbl2 = Label(frame, text=f"Frame {len(frames) + 1} label 2")
lbl1.pack()
lbl2.pack()
frame.pack(padx=5, pady=5)
frame_number = len(frames)
lbl1.bind('<B1-Motion>', lambda event: MoveWindow(event, frame_number))
lbl2.bind('<B1-Motion>', lambda event: MoveWindow(event, frame_number))
frame.bind('<B1-Motion>', lambda event: MoveWindow(event, frame_number))
frames.append(frame)
labels.append(lbl1)
labels.append(lbl2)
def MoveWindow(event, frame_number):
global root, frames
root.update_idletasks()
f = frames[frame_number]
x = f.winfo_width()/2
y = f.winfo_height()*1.5
f.place(x=event.x_root-x, y=event.y_root-y)
root = Tk()
root.geometry("500x500")
frames = []
labels = []
btn = Button(root, text="Open Frame", command=open_frame)
btn.pack()
root.mainloop()

Tkinter scroll bar will not control entry widgets

My scroll bar is supposed to be a child of Canvas_2 and controlling the y-value of Canvas_3. However it is not working as intended. The scroll bar doesn't move up or down, and there is also a blue region that shouldn't be visible. Any ideas on what I'm missing here? I really appreciate your time.
import tkinter as tk
from PIL import ImageTk, Image
# To initialize tkinter, we have to create a Tk root widget,
# which is a window with a title bar and other decoration
# provided by the window manager.
# The root widget has to be created before any other widgets
# and there can only be one root widget.
root = tk.Tk()
# The weight of a row or column determines how much of the
# available space a row or column should occupy relative to
# the other rows or columns. For example, a column with a
# weight of 2 will be twice as wide as a column with a
# weight of 1, assuming there's space for the widgets to fit.
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
load1 = Image.open("example.jpg")
render1 = ImageTk.PhotoImage(load1)
# Creating a class for filling each row
def makeRow(top, img, row):
r = row
if row == 0:
c1 = "#75dce1"
c2 = "#75dce1"
e7 = tk.Entry(top, bg=c2).grid(row=r, column=6, sticky="news")
e8 = tk.Entry(top, bg=c2).grid(row=r, column=7, sticky="news")
else:
c1 = "#a9d08e"
c2 = "#8dd1bf"
img = tk.Label(top, image=render1, bg="green").grid(row=r, column=6, sticky="news")
e1 = tk.Entry(top, bg=c1).grid(row=r, column=0, sticky="news")
e2 = tk.Entry(top, bg=c1).grid(row=r, column=1, sticky="news")
e3 = tk.Entry(top, bg=c1).grid(row=r, column=2, sticky="news")
e4 = tk.Entry(top, bg=c1).grid(row=r, column=3, sticky="news")
e5 = tk.Entry(top, bg=c2).grid(row=r, column=4, sticky="news")
e6 = tk.Entry(top, bg=c2).grid(row=r, column=5, sticky="news")
# load1 = Image.open(img)
# render1 = ImageTk.PhotoImage(load1)
# The canv_1 is a child of the parent "root"
# canv_1 contains: canv_2 (frozen top row) and canv_3 (bottom rows with a vertical scroll)
canv_1 = tk.Canvas(root, bg="blue")
canv_1.grid_rowconfigure(0, weight=1)
canv_1.grid_rowconfigure(1, weight=10)
canv_1.grid_columnconfigure(0, weight=1)
canv_1.grid(row=0, column=0, sticky = "news")
canv_1.grid(row=1, column=0, sticky = "news")
# The canv_2 is a child of the parent "canv_1"
canv_2 = tk.Canvas(canv_1, bg="blue")
canv_2.grid_rowconfigure(0, weight=1)
canv_2.grid_rowconfigure(1, weight=1)
canv_2.grid_columnconfigure(0, weight=1)
canv_2.grid(row=0, column=0, sticky = "news")
canv_2.grid(row=1, column=0, sticky = "news")
# The canv_3 is a child of the parent "canv_2"
canv_3 = tk.Canvas(canv_2, bg="blue")
canv_3.grid(row=1, column=0, sticky="news")
# canv_3.grid_rowconfigure((1,2,3,4,5), weight=1)
# canv_3.grid_columnconfigure((1,2,3,4,5), weight=1)
slides = []
for i in range(10):
slides.append(i)
if i==0:
slides[i] = makeRow(canv_3,"", 0)
else:
slides[i] = makeRow(canv_3, "example.jpg", i)
# Create Scrollbar
vsb = tk.Scrollbar(canv_2, orient="vertical", command=canv_3.yview)
vsb.grid(row=1, column=1, sticky='ns')
canv_2.configure(yscrollcommand=vsb.set)
canv_2.config(scrollregion=canv_3.bbox("all"))
canv_2.configure(scrollregion=(0, 0, 5000, 5000))
root.mainloop()
This is the actual output:
This is the desired design & output:
There are at least three fundamental problems with the code. The biggest problem is that you are adding widgets to canv_3 using grid. A canvas can't scroll items added to a canvas with grid. It will only scroll items added with the "create" methods of the canvas (create_window, create_text, etc).
The second problem is that you never define a proper scrollregion for canv_3, so even if you added the items with create_window, tkinter wouldn't know what the scrollable region is.
The third problem is that scrollbars require a two-way configuration. The scrollbar command needs to call the yview method of the widget, and the widget's yscrollcommand option needs to call the set method of the scrollbar.

Can I stack a Frame on top of a canvas? (Tkinter)

I'm new to coding and am attempting to make some sort of EPOS type system just as a project for the shop I work in. I want to stack a frame to have a keypad for a log in code on top of a background image, just for the start of the program. Essentially no matter how I try to stack the different Tkinter widgets, it never seems to work.
I've tried placing the canvas which holds the image in the main Tk() and then place the frame on top of that, to then use a grid structure to build the keypad put that didn't work.
I tried different combinations of which widget parents which other widget etc, and couldn't get anything to work. It usually ended up with no frame visible, and the 1920x1080 image being pushed to the bottom right of the screen.
screen_width = 1920
screen_height = 1080
screen_geometry = '{}x{}'.format(screen_width, screen_height)
main_window = Tk()
main_window.title('Shop')
main_window.resizable(0,0)
main_window.geometry(screen_geometry)
background_image = PhotoImage(master=C, file='logo.png')
C = Canvas(main_window, bg="blue", height=screen_height, width=screen_width)
background_label = Label(C, image=background_image)
background_label.place(x=0, y=0, relwidth=1, relheight=1)
C.place(x=0, y=0, relwidth=1, relheight=1)
login_window = Frame(main_window, borderwidth=5, relief=GROOVE)
login_window.config(width=10, height=10)
login_window.place(x=0, y=0, relwidth=1, relheight=1)
test_button = Button(login_window, text="test")
test_button.grid(column=0, row=0)
main_window.mainloop()
I expected the logo to be placed underneath the frame, and then I'd be able to use the frame with a normal grid structure, but it didn't seem to work at all.
This code is messy and poor, so some constructive criticism and help overall would be lovely.
Thank you.
From my understanding, you want to have a login window with a background image and a keypad at the center of the window. Below is a sample code:
from tkinter import *
screen_width = 1920 // 2
screen_height = 1080 // 2
screen_geometry = '{}x{}'.format(screen_width, screen_height)
main_window = Tk()
main_window.title('Shop')
main_window.resizable(0, 0)
main_window.geometry(screen_geometry)
# background image
background_image = PhotoImage(file='logo.png')
background_label = Label(main_window, image=background_image)
background_label.place(x=0, y=0, relwidth=1, relheight=1)
# keypad at the center of window
login_frame = Frame(main_window)
login_frame.place(relx=0.5, rely=0.5, anchor=CENTER)
display = Label(login_frame, bg='black', font=('', 20))
display.grid(row=0, column=0, columnspan=3, sticky='ew')
def input_digit(n):
print(n)
font = ('', 16, 'bold')
numpad = []
for number in range(9):
row = number // 3
col = number % 3
btn = Button(login_frame, text=number+1, font=font, width=5, height=2)
btn.grid(row=row+1, column=col)
btn.config(command=lambda n=number+1:input_digit(n))
numpad.append(btn)
main_window.mainloop()

Python 3: Positions of button and input text in a text box

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

python3 & tkinter - creating a zoom button to zoom on the loaded image

I would like to create a zoom button. On clicking on that zoom button, the image would be zoomed in by a factor represented by an integer (1,2,3,4,5...). With this piece of code, by clicking on the zoom button, another panel is created underneath the already loaded picture. Inside it is blank. What would be needed is to:
1. kill the first (non-zoomed window) and 2. load the zoomed image on the updated panel
from tkinter import *
from tkinter.filedialog import askopenfilename
import tkinter as tk
event2canvas = lambda e, c: (c.canvasx(e.x), c.canvasy(e.y))
root = Tk()
#setting up a tkinter canvas with scrollbars
frame = Frame(root, bd=2, relief=SUNKEN)
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)
xscroll = Scrollbar(frame, orient=HORIZONTAL)
xscroll.grid(row=1, column=0, sticky=E+W)
yscroll = Scrollbar(frame)
yscroll.grid(row=0, column=1, sticky=N+S)
canvas = Canvas(frame, bd=0, xscrollcommand=xscroll.set,yscrollcommand=yscroll.set)
canvas.grid(row=0, column=0, sticky=N+S+E+W)
xscroll.config(command=canvas.xview)
yscroll.config(command=canvas.yview)
frame.pack(fill=BOTH,expand=1)
#adding the image
image_str="image.png"
image = tk.PhotoImage(file=image_str)
image = image.zoom(1,1)
canvas.create_image(0,0,image=image,anchor="nw")
canvas.config(scrollregion=canvas.bbox(ALL))
def zoomin():
root = Tk()
frame = Frame(root, bd=2, relief=SUNKEN)
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)
xscroll = Scrollbar(frame, orient=HORIZONTAL)
xscroll.grid(row=1, column=0, sticky=E+W)
yscroll = Scrollbar(frame)
yscroll.grid(row=0, column=1, sticky=N+S)
canvas = Canvas(frame, bd=0, xscrollcommand = xscroll.set, yscrollcommand = yscroll.set)
canvas.grid(row=0, column=0, sticky=N+S+E+W)
xscroll.config(command=canvas.xview)
yscroll.config(command=canvas.yview)
frame.pack(fill=BOTH,expand=1)
image = tk.PhotoImage(file=image_str)
image = image.zoom(1,1)
canvas.create_image(0,0,image=large_img,anchor="nw")
canvas.config(scrollregion=canvas.bbox(ALL))
toolbar = Frame(root, bg="blue")
insertButt = Button(toolbar, text="zoomin", command=lambda:zoomin())
insertButt.pack(side = LEFT, padx=2, pady=2)
toolbar.pack(side=TOP, fill = X)
#function to be called when mouse is clicked
def printcoords(event):
#outputting x and y coords to console
print (event.x,event.y)
#mouseclick event
canvas.bind("<Button 1>",printcoords)
#mouseclick event
canvas.bind("<ButtonPress-1>",printcoords)
canvas.bind("<ButtonRelease-1>",printcoords)
root.mainloop()
I would like to thank #Symon for his stackoverflow question. I largely inspired myself from his code
Well, the reason that the function zoomin(img) does not work properly is that it returns in the first line:
def zoomin(img):
return # Function returns here
... rest of code is never executed
I suspect this is due to the function being run when you create the button, not when you press it. Try cretating the button in this way instead:
insertButt = Button(toolbar, text="zoomin", command=lambda:zoomin(img))
Now the button will call zoomin(img) when it's pressed and not when the button is created.
Zooming with Tkinter
PhotoImage zoom only allows integer values, wich makes it a bit limited. But here's an example:
from tkinter import *
root = Tk()
root.geometry('300x200')
field = Canvas(root, bg='tan1', highlightthickness=0)
field.grid(sticky='news')
photo = PhotoImage(file='test.gif')
field.create_image(0, 0, image=photo, anchor='nw')
scale = 1
def zoom(event=None):
global scale, photo
scale = scale * 2
field.delete('all')
photo = photo.zoom(x=scale, y=scale)
field.create_image(0, 0, image=photo, anchor='nw')
field.image = photo
root.bind('z', zoom) # Bind "z" to zoom function
root.mainloop()
If you want to zoom by float you'll have to import a module for that. Pillow seems popular. But I haven't worked with any of them so you'll have to research them yourself.

Resources