Tkinter: Buttons can't be added on resizable PhotoImage - python-3.x

Buttons can't be added on resizable image label in Tkinter. I am trying to add a resizable image that is easily showing up. Also, I am trying to add buttons on the top of it which seems to me as an issue.
Here's the code:
from tkinter import *
from tkinter import ttk
from PIL import Image, ImageTk
root = Tk()
root.title("Speaker Recognition")
root.geometry('1280x800+0+0')
def resize_image(event):
new_width = event.width
new_height = event.height
image = copy_of_image.resize((new_width, new_height))
photo = ImageTk.PhotoImage(image)
label.config(image = photo)
label.image = photo
## Resizable Image
image = Image.open('speakerid.gif')
copy_of_image = image.copy()
photo = ImageTk.PhotoImage(image)
label = Label(root, image=photo)
label.place(x=0, y=0, relwidth=1, relheight=1)
label.bind('<Configure>', resize_image)
label.pack(fill=BOTH, expand = YES)
##Adding Buttons
train_button = Button(root, bg="red", text='Train the machine')
train_button.place(x=0, y=0, relwidth=1, relheight=1)
train_button.pack()
test_button = Button(root, bg="red", text='Test the machine')
test_button.place(x=0, y=0, relwidth=1, relheight=1)
test_button.pack()
quit = Button(root, bg="red", text="Quit", command=root.destroy)
quit.place(x=0, y=0, relwidth=1, relheight=1)
quit.pack()
root.mainloop()
Any idea how to fix this?

You cannot both place and pack widgets. In this case, just place everything. You have the image label filling the container at the lower z-order and if you then place your labels and discard the pack() method call they should appear fine as their z-order is higher than the label. Otherwise lower and tkraise can be used to adjust the ordering so the buttons appear on top of the label.

Related

Implement scrollbar in Canvas

So I wanted to implement a scrollbar on the mainframe in Tkinter however that is not possible thus I have to use Canvas. With canvas, I am able to implement a scrollbar and use other widgets accordingly. Everything worked fine until I attempted to add widgets into the Canvas.
Error Faced: Scrollbar looks off and unable to scroll
So what did I do wrong and how can I fix it?
Code (Python 3.8.2)
from tkinter import *
root = Tk()
root.geometry("950x600")
# create canvas
canvas = Canvas(root, width=932, height=600, borderwidth=0, highlightthickness=0, bg="black")
canvas.grid()
# create a scrollbar
vsb = Scrollbar(root, orient="vertical", command=canvas.yview)
vsb.grid(row=0, column=1, sticky='ns')
canvas.configure(yscrollcommand=vsb.set)
# Test the ability to scroll
for x in range(30):
Label(canvas, text="test").grid(row=x)
root.mainloop()
Note: I only want to add other widgets, including scrollbar using only grid manager
Things to add
#1 a frame inside a canvas
#2 binding an event (configure) that changes the canvas view thing with scroll
#3 create a window with the given frame
from tkinter import *
root = Tk()
root.geometry("950x600")
# create canvas
canvas = Canvas(root, width=932, height=600, borderwidth=0, highlightthickness=0, bg="black")
vsb = Scrollbar(root, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
# create a scrollbar
f=Frame(canvas)#1
canvas.grid()
canvas.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))#2
canvas.create_window((0,0),anchor='nw',window=f,width=932)#3
vsb.grid(row=0, column=1, sticky='ns')
# Test the ability to scroll
for x in range(300):
Label(f, text="test").grid(row=x)
root.mainloop()

How do I overlay these buttons on top of an image for a background in Python Tkinter

Currently, I'm attempting to create a simple main menu for a game using Tkinter as a simple GUI since it's a simple RPG game and Python however the image overlays the buttons in all circumstances.
I've tried using some other solutions like placing them or creating a window but I can't find a straight answer of how to make that either.
import tkinter
from tkinter import *
from PIL import ImageTk, Image
(PIL is from when I was using a JPG before.)
root = Tk()
content = ttk.Frame(root)
root.geometry("600x600")
background = ImageTk.PhotoImage(Image.open("bred.png"))
canvas = tkinter.Canvas(root, width=580, height=600)
content.grid(column=0, row=0)
Btn1 = Button(content, text="Play", width=5, height=1)
Btn2 = Button(content, text="Kill me", width=7, height=1, command =
root.quit)
backgroundlabel = tkinter.Label(root, image=background)
backgroundlabel.image = background
backgroundlabel.place(x=0, y=0, relwidth=1, relheight=1)
Btn1.grid(row=1, column=2, padx=(130))
Btn1.columnconfigure(1, weight=1)
Btn1.rowconfigure(1, weight=1)
Btn2.grid(row=1, column=3, pady=(130))
Btn2.columnconfigure(3, weight=1)
Btn2.rowconfigure(1, weight=1)
root.mainloop()
Currently your background's master is set to root while your buttons are set to a frame. The first thing you need to do is set both to the same master, i.e. changing background master to content:
backgroundlabel = tk.Label(content, image=background)
Next you need to deal with the stacking order. You can call widget.lift() to raise the buttons to top:
Btn1.grid(row=1, column=2, padx=(130))
...
Btn1.lift()
Btn2.grid(row=1, column=3, pady=(130))
...
Btn2.lift()

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()

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.

Getting a pop up canvas to take focus

How do I transfer keyboard control from one canvas to another
self.canvas.bind('<FocusOut>')
self.canvas2.bind('<FocusIn>')
doesn't work, the focus is still on canvas one and the keyboard still acts on canvas one not canvas two.
As well
self.canvas2.focus_set()
still keeps the focus on self.canvas and not on self.canvas2.
focus_set is how you give focus to a widget.
Here's an example that shows two canvases, and sets the focus when you click one of the canvases. When you press the "s" key, a square will be drawn on whichever canvas has the focus.
import tkinter as tk
import random
def draw_square(event):
x0 = random.randint(30, 370)
y0 = random.randint(30, 170)
size = random.randint(10, 30)
event.widget.create_rectangle(x0, y0, x0+size, y0+size, fill="red")
def give_focus(event):
event.widget.focus_set()
event.widget.configure(background="bisque")
def lose_focus(event):
event.widget.configure(background="white")
root = tk.Tk()
label = tk.Label(root, text="Click to focus a canvas, press 's' to draw a square")
canvas1 = tk.Canvas(root, width=400, height=200, background="white",
borderwidth=1, relief="raised")
canvas2 = tk.Canvas(root, width=400, height=200, background="white",
borderwidth=1, relief="raised")
label.pack(side="top", fill="x")
canvas1.pack(fill="both", expand=True)
canvas2.pack(fill="both", expand=True)
for canvas in (canvas1, canvas2):
canvas.bind("<FocusOut>",lose_focus)
canvas.bind("<1>", give_focus)
canvas.bind("<s>", draw_square)
root.mainloop()

Resources