I am creating a simple tkinter application. When the user presses a button an image appears. If they press it again another image appears etc. So I am wanting a scroll bar so that the user can scroll to the images that appear off the page. I am not sure what I have done wrong but the scroll bar is greyed out and is not working.
import calendar
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry('800x800')
x = 30
y = 30
box_image = tk.PhotoImage(file='apple.png')
### SCROLL BAR ###
# create a main frame
main_frame = tk.Frame(root)
main_frame.pack(fill='both', expand=1)
# create a canvas
my_canvas = tk.Canvas(main_frame)
my_canvas.pack(side='left', fill='both', expand=1)
# add a scrollbar to the canvas
my_scrollbar = ttk.Scrollbar(main_frame, orient='vertical', command=my_canvas.yview)
my_scrollbar.pack(side='right', fill='y')
# configure the canvas
my_canvas.configure(yscrollcommand=my_scrollbar.set)
my_canvas.bind('<Configure>', lambda e: my_canvas.configure(scrollregion = my_canvas.bbox("all")))
# create Another frame inside the canvas
second_frame = tk.Frame(my_canvas)
# add that new frame to a window in the canvas
my_canvas.create_window((0,0), window=second_frame, anchor="nw", width="10000")
def display():
global x , y
panel2 = tk.Label(main_frame, image=box_image, bg='#f7f6f6')
panel2.place(x=x, y=y)
x = x
y = y+200
button = tk.Button(second_frame, text="click me", command=display)
button.place(x=0, y=0)
label = tk.Label(second_frame, text="label", bg="blue")
label.pack(fill="x", padx=50)
new_frame = tk.Frame(second_frame, bg='black', height=110)
new_frame.pack(fill="x")
root.mainloop()
You need to put the image in second_frame, not in main_frame.
Related
Minimal reproducable Example
from tkinter import *
def test(event):
print(event.widget)
window = Tk()
window.geometry("600x600")
window.bind("<Motion>", test)
frame = Frame(window, bg="red", width=200, height=200)
frame.pack()
frame.bind("<Motion>", test)
window.mainloop()
I want to call the function "test" from different widgets when i move over them. Instead of that, when i hover over the frame, both the window and the frame print that im hovering over the frame, which is not the behaviour i need. Can someone help me achieve the right behaviour?
I think your confusion is in the moment of interpreting when the mouse pointer entered the widget or left the widget. Instead of Motion let's use for example Leave and Enter events to better understand what happens.
I have taken the liberty of including some labels that show which widget we enter and left at each moment.
from tkinter import *
def test_enter(event):
enter.set(event.widget)
def test_left(event):
left.set(event.widget)
window = Tk()
window.geometry("300x300")
window.bind("<Enter>", test_enter)
window.bind("<Leave>", test_left)
frame = Frame(window, bg="red", width=200, height=200)
frame.pack()
frame.bind("<Enter>", test_enter)
frame.bind("<Leave>", test_left)
label1 = Label(frame, text="Enter")
label1.place(relx=0.1, rely=0.4, x= 0.2, y=0.2)
enter = StringVar()
label_enter = Label(frame, textvariable=enter)
label_enter.place(relx=0.3, rely=0.4, x= 0.6, y=0.2)
label2 = Label(frame, text="Leave")
label2.place(relx=0.1, rely=0.6, x= 0.2, y=0.2)
left = StringVar()
label_left = Label(frame, textvariable=left)
label_left.place(relx=0.3, rely=0.6, x= 0.6, y=0.2)
window.mainloop()
Remember that the Frame is inside the Window. I mean that when you enter the Window you have not left root Window, you will continue to be in Window. Or if you enter inside a Label you have not left the Frame.
You could use lambda to pass the widget when the function is run
def test(event, widget):
print(widget)
window = Tk()
window.geometry("600x600")
window.bind("<Motion>", test)
frame = Frame(window, bg="red", width=200, height=200)
frame.pack()
frame.bind("<Motion>", lambda: test(frame))
window.mainloop()
You just need to change two lines
def test(event, widget):
print(widget)
and
frame.bind("<Motion>", lambda: test(frame))
I recently made 2 functions, add_scrollbar and update_scroll_region, which adds a scrollbar to a given frame and updates the scroll region when widgets in that frame change.
The frame in which I am adding a scrollbar is a notebook tab. The functions work as intended, but when I switch off a tab with the scrollbar (and only the ones with the scrollbar) and come back to it, all the widgets are gone. If I move my mouse off of the tab label, all the widgets re-appear. I am not exactly sure what it is about these 2 functions that could be causing this kind of behavior. I have provided a simplified example below. In this example, TAB1 has the scrollbar, and TAB2 does not. You will notice that switching from TAB2 to TAB1 hides the button in TAB1 until the mouse is moved.
from tkinter import *
from tkinter import ttk
root = Tk()
root.geometry("1200x1200")
def add_scrollbar(outer_frame):
canvas = Canvas(outer_frame)
canvas.pack(side=LEFT, fill=BOTH, expand=1)
information_frame = Frame(canvas)
canvas.create_window((0, 0), window=information_frame)
scrollbar = ttk.Scrollbar(outer_frame, orient=VERTICAL, command=canvas.yview)
scrollbar.pack(side=RIGHT, fill=Y)
canvas.configure(yscrollcommand=scrollbar.set)
return information_frame, canvas
def update_scroll_region(canvas):
global root
root.update()
bbox = canvas.bbox("all")
x, y, width, height = bbox
if height < canvas.winfo_height():
bbox = x, y, width, canvas.winfo_height()
canvas.configure(scrollregion=bbox)
return
def create_example():
global root
notebook = ttk.Notebook(root, height=1200, width=1500)
notebook.pack(pady=10)
my_outer_frame_1 = Frame(root)
my_outer_frame_1.pack(fill=BOTH, expand=1)
notebook.add(my_outer_frame_1, text="TAB1")
inner_frame_1, my_canvas_1 = add_scrollbar(my_outer_frame_1)
my_outer_frame_2 = Frame(root)
my_outer_frame_2.pack(fill=BOTH, expand=1)
notebook.add(my_outer_frame_2, text="TAB2")
Label(my_outer_frame_2, text="This always shows").pack()
# ^^^ Sets up a notebook with 2 tabs
changing_frame = Frame(inner_frame_1, borderwidth=4) # this is the frame that will be changing its contents
changing_frame.pack(side=LEFT, anchor="n")
display_frame(changing_frame, my_outer_frame_1, my_canvas_1)
# this method re-displays the changing frame depending on the specified size ('big' or 'small')
root.mainloop()
return
def display_frame(frame, outer_frame, canvas, size='small'):
for widget in frame.winfo_children():
widget.destroy()
if size == 'small':
Button(frame, text="This button is gone until the mouse is moved",
command=lambda this_frame=frame: display_frame(this_frame, outer_frame, canvas, size='big')).grid(row=0,
column=0)
elif size == 'big':
Button(frame, height=5, width=5, text="Hide",
command=lambda this_frame=frame: display_frame(this_frame, outer_frame, canvas, size='small')).grid(
row=0, column=0)
for n in range(1, 100):
Label(frame, text="Other Stuff!").grid(row=n, column=0)
update_scroll_region(canvas)
return
if __name__ == '__main__':
create_example()
I'm using Tkinter to create an application, and with this Dashboard class, I'm trying to get a pop out window to show another canvas, so I can use create_image and tag_bind on the pop out. The result I'm currently getting is that the second canvas appears over the first canvas instead of in the pop out window.
import tkinter as tk
from tkinter import *
class Dashboard(tk.Tk):
"""
Configures, and displays the Dashboard
"""
def __init__(self):
tk.Tk.__init__(self)
self.config(width=1440, height=1024)
canvas = tk.Canvas(self, bg="#343333", height=1024, width=1440, bd=0, highlightthickness=0, relief="ridge")
canvas.place(x=0, y=0)
# Captures the background image for the canvas
image_path = "dashboard_background.png"
self.background_img = tk.PhotoImage(file=image_path)
canvas.create_image(0, 0, anchor='nw', image=self.background_img)
def logoutbuttonClicker():
pop = Toplevel(self)
pop.geometry('537x273')
pop.config(height=273, width=537)
logout_canvas = tk.Canvas(canvas, bg="#ffffff", height=273, width=537, bd=0, highlightthickness=0, relief="ridge")
logout_canvas.place(x=0, y=0)
self.logout_background_img = PhotoImage(file=f"logout_background.png")
logout_canvas.create_image(268.5, 136.5, anchor='nw', image=self.logout_background_img)
self.logout_yes_img = PhotoImage(file=f"logout_yes.png")
self.logout_no_img = PhotoImage(file=f"logout_no.png")
logout_image_path = "dashboard_logout.png"
self.logout_image = tk.PhotoImage(file=logout_image_path)
logoutButton = canvas.create_image(45, 950, anchor='nw', image=self.logout_image)
canvas.tag_bind(logoutButton, "<ButtonRelease-1>", lambda event: logoutbuttonClicker())
def main():
app = Dashboard()
app.mainloop()
if __name__ == '__main__':
main()
This is how it currently appears in the UI
I am trying to add a scroll bar into my tkinter application. It is appearing, however it is greyed out and is not able to scroll and I am not sure why despite the fact that I have many widgets beyond the height of the page. Any help would be appreciated.
import calendar
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry('800x800')
x = 30
y = 30
box_image = tk.PhotoImage(file='apple.png')
### SCROLL BAR ###
# create a main frame
main_frame = tk.Frame(root)
main_frame.pack(fill='both', expand=1)
# create a canvas
my_canvas = tk.Canvas(main_frame)
my_canvas.pack(side='left', fill='both', expand=1)
# add a scrollbar to the canvas
my_scrollbar = ttk.Scrollbar(main_frame, orient='vertical', command=my_canvas.yview)
my_scrollbar.pack(side='right', fill='y')
# configure the canvas
my_canvas.configure(yscrollcommand=my_scrollbar.set)
my_canvas.bind('<Configure>', lambda e: my_canvas.configure(scrollregion = my_canvas.bbox("all")))
# create Another frame inside the canvas
second_frame = tk.Frame(my_canvas)
# add that new frame to a window in the canvas
my_canvas.create_window((0,0), window=second_frame, anchor="nw")
def display():
global x , y
panel2 = tk.Label(main_frame, image=box_image, bg='#f7f6f6')
panel2.place(x=x, y=y)
x = x
y = y+200
button = tk.Button(main_frame, text="click me", command=display)
button.place(x=0, y=0)
for thing in range (100):
tk.Button(main_frame, text=f'Button {thing}Yo!').pack()
root.mainloop()
For the frame-in-a-canvas trick to work, you must add the buttons to the inner frame:
for thing in range (100):
tk.Button(second_frame, text=f'Button {thing}Yo!').pack()
# ^^^^^^^^^^^^
in this code i try to add two frames one on the left and second on the right and i need to make the left frame fixed and add scrollbar on the right frame
from tkinter import *
from tkinter import ttk
from tkinter.ttk import *
win_width = 1200
win_hight = 720
root=Tk()
root.geometry("{}x{}".format(str(win_width),str(win_hight)))
frame_style = Style()
frame_style.configure('blue.TFrame', background='#1d96f4',highlightthickness=0)
frame_style.configure('white.TFrame', background='#FFFFFF',highlightthickness=0)
frame1 = ttk.Frame(root, style='blue.TFrame')
frame1.grid(row=0,column=0)
frame1.config(width=win_width*(50/100.0),height=win_hight, relief=RIDGE)
frame1.grid_propagate(0)
frame2 = ttk.Frame(root, style='white.TFrame')
frame2.grid(row=0,column=2)
frame2.config(width=win_width*(50/100.0),height=win_hight, relief=RIDGE)
frame2.grid_propagate(0)
my_canvas = Canvas(frame2, background="white",width=1100,height=720)
my_canvas.pack(side=LEFT,fill=Y)
my_scrollbar = ttk.Scrollbar(frame2, orient=VERTICAL , command=my_canvas.yview)
my_scrollbar.pack(side=RIGHT, fill=Y)
my_canvas.configure(yscrollcommand=my_scrollbar.set)
my_canvas.bind('<Configure>', lambda e: my_canvas.configure(scrollregion = my_canvas.bbox("all")))
second_frame = Frame(my_canvas)
my_canvas.create_window((1200,720), window=second_frame)
for i in range(0,50):
title_label = Label(second_frame, text="Test",background='#1d96f4',foreground="#FFFFFF",font=('Helvetica', 22, 'bold'))
title_label.place(x=20, y=40*i)
root.mainloop()
I have tried various methods but have not been able to set the look properly and I don't want to use "pack" at least inside the right frame