Getting a pop up canvas to take focus - python-3.x

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

Related

Why my tkinter frame is not on the top of the canvas and canvas can still be scrolled up?

I'm creating a GUI tool and while working on it, I faced an issue that I couldn't figure out why it is happening.
I have a scrollable frame inside a canvas, however that frame is not on the top side of the canvas "even though I want it on the top side" and I noticed that the canvas can still be scrolled up above the frame (frame background is green) which is I consider a wrong behavior.
It doesn't matter how many times, I checked the code and revised/edited it, I still cannot figure it out, so I decided to check here for any hints and thank you in advance.
My code is as follow
import tkinter.ttk as ttk
from tkinter import *
root = Tk()
root.title("Checklist Buddy")
root.config(padx=10, pady=5)
root.geometry("800x500")
top_frame = Frame(root, bg="black")
top_frame.grid(row=0, column=0, sticky="NSEW")
mf = Frame(root, bg="brown")
mf.grid(row=1, column=0, sticky="NEWS")
canvas = Canvas(mf, bg="yellow")
canvas.grid(row=0, column=0, sticky="NEWS")
yscrollbar = ttk.Scrollbar(mf, command=canvas.yview)
yscrollbar.grid(row=0, column=1, sticky="ns")
canvas.bind('<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
canvas.configure(yscrollcommand=yscrollbar.set)
bpo_frame = Frame(canvas, background="green")
win = canvas.create_window((0, 0), window=bpo_frame, height=100)
def _on_mousewheel(event):
canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
def onCanvasConfigure(event):
canvas.itemconfigure(win, width=event.width)
canvas.bind('<Configure>', onCanvasConfigure, add="+")
canvas.bind_all("<MouseWheel>", _on_mousewheel)
root.columnconfigure("all", weight=1)
root.rowconfigure("all", weight=1)
mf.columnconfigure(0, weight=1)
# mf.rowconfigure("all", weight=1)
root.mainloop()
Below is a picture to show what I mean, there shall be no empty yellow space above the green window (no scroll up shall be there as the window shall be on the top side)
The main issue here is the scrollregion of the canvas, which is set to a region with negative y-coordinates. If you want the point (0,0) to be at the top of the canvas, you need to define it. And don't change it once the canvas is reconfigured.
Also, the canvas window needs an anchor in the top left corner. Otherwise, it is centered on (0,0) and reaches half into the negative x and y coordinates.
If you change the lines 15-25 as follows, the behavior is as expected:
(Personally, I would also set the width of the window equal to the width of the canvas before scrolling the first time instead of binding this change to the <Configure>. But you may have a reason for this)
canvas = Canvas(mf, bg="yellow",scrollregion=(0, 0, 1000, 1000))
canvas.grid(row=0, column=0, sticky="NEWS")
yscrollbar = ttk.Scrollbar(mf, command=canvas.yview)
yscrollbar.grid(row=0, column=1, sticky="ns")
#canvas.bind('<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
canvas.configure(yscrollcommand=yscrollbar.set)
bpo_frame = Frame(canvas, background="green")
win = canvas.create_window((0, 0), window=bpo_frame, height=100, anchor=NW)

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

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.

Tkinter: Buttons can't be added on resizable PhotoImage

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.

Resources