There is a master window named top_wl. And frame_wl is created in it.
I have set of labels that are created in a frame_wl (which is on a canvas_wl) and also I am creating another small frame (fram_s) in master window (top_wl) which is on the separate canvas( canvas_s). another frame is created in the canvas_s ( named frame_s1). And i have created one label on fram_s( small frame). have set one horizontal scroll bar for canvas_wl alone .I have set both the canvas_wl and canvas_s on single vertical scroll bar (named : scroll_bar_v). The idea behind this is to freeze the labels in the small frame while moving horizontally where as it has moved with vertical scroll bar.
Now the problem is the labels on the small frame ( fram_s) is freezed on both vertical and horizontal movement. can some body help me to solve the problem? or please tell me where I am going wrong ?
top_wl = tk.Tk()# tk.Tk()
top_wl.title("Work Shop Layout")
fram_wl =tk.Frame(top_wl,width=1500,height=5000,bg = 'khaki1',relief= SUNKEN)
# Creating small frame
fram_s = tk.Frame(top_wl,width = 300,height = 500,bg = 'sky blue')
fram_s.place( x = 15, y = 90)
canvas_s = tk.Canvas(fram_s)
frame_s1 = tk.Frame(canvas_s)
l_12 = tk.Label(fram_s, text = 'Hello There',bd = 2, width = 30,bg = 'white',font = ('Arial',10,'bold'),relief = "solid")
l_12. place(x = 10, y = 30)
# finished samll frame
canvas_wl = tk.Canvas(fram_wl,width=1500,height=4000,bg = 'lime green')
# top frame portion
scroll_bar_v = tk.Scrollbar(fram_wl,orient='vertical')
scroll_bar_v.config(command = canvas_wl.yview )
scroll_bar_h = Scrollbar(fram_wl,orient='horizontal')
scroll_bar_h.config(command = canvas_wl.xview)
scroll_bar_h.pack( side = BOTTOM, fill = X )
t1 = tk.Label(fram_wl)#, text = "Hello There: Number_1",width = 50,height = 100,bg = 'Sky blue',font = ('Arial',10,'bold'),relief = "sunken") #(x = 710, y = 50)
scroll_bar_v.pack( side = RIGHT, fill = Y )
t1.pack(side = RIGHT)
scrollable_frame_wl = tk.Frame(canvas_wl,bg = 'moccasin')#, bg = 'cyan2')
canvas_wl.create_window(0, 0, window=scrollable_frame_wl, anchor='nw')
left_wl = tk.Frame(scrollable_frame_wl, borderwidth=2, height = 5000,width = 10000,relief="sunken") #
left_wl. pack(side = 'left',padx=10, pady=10)
left_wl.pack_propagate(0)
scrollable_frame_wl.update()
canvas_s.configure(yscrollcommand = scroll_bar_h.set,scrollregion = canvas_s.bbox("all"))
canvas_s.create_window((0,0),window =frame_s1, anchor = 'nw' )
canvas_wl.configure(xscrollcommand= scroll_bar_h.set,yscrollcommand= scroll_bar_v.set,scrollregion=canvas_wl.bbox("all"))
canvas_wl.pack(fill = 'both')
fram_wl.pack()
top_wl.mainloop()
This code scroll vertically both canvas and horizontally only bigger canvas.
There was mess in code so I organized code in different way to make some order.
First create scrollbars (which will be used by both canvas)
Next I create bigger frame with canvas and inner frame.
Next in the same way I create smaller frame with canvas and inner frame.
Next I add some elements to inner frames - in both I add label at the top and at the bottom of inner frame. This way I will see if it scroll to the end.
And finally I assign scrollbars to canvas to move them. Next I assign canvas to scrollbars to resize scrollbars to correct size.
And it works
There is only one problem. At this moment both frames shows only 1/3 of canvas height - so vertical scrollbar need to show slider with the same size (1/3 height) - but when you resize window then bigger frame show more canvas and scrollbar also show longer slider but smaller frame still show 1/3 of canvas and it would need smaller slider - and it would make conflict. It would display longer slider for short time (for bigger frame) and next longer slider for longer time (for shorter frame). Resizing also makes problem when you scroll down - first it shows bottom label in bigger frame but it still need mouse move to show bottom label in smaller frame.
I don't see good solution for this conflict - eventually you would have to resize smaller frame to show the same proporiton of canvas.
import tkinter as tk
top_wl = tk.Tk()
top_wl.title("Work Shop Layout")
# --- scrolls outside big frame (directly in window) ---
scroll_bar_v = tk.Scrollbar(top_wl, orient='vertical')
scroll_bar_v.pack(side='right', fill='y')
scroll_bar_h = tk.Scrollbar(top_wl, orient='horizontal')
scroll_bar_h.pack(side='bottom', fill='x')
# --- big frame (directly in window) ---
# external frame
fram_wl = tk.Frame(top_wl, width=500, height=500, bg='khaki1', relief='sunken')
fram_wl.pack(side='right', expand=True, fill='both')
fram_wl.pack_propagate(False) # don't resize external frame to canvas size
# inne canvas with size bigger then external frame
canvas_wl = tk.Canvas(fram_wl, width=1500, height=1500, bg='lime green')
canvas_wl.pack(fill='both')
# inner frame with size bigger then frame (scrollbars will use this size to scroll it)
scrollable_frame_wl = tk.Frame(canvas_wl, width=1500, height=1500, bg='moccasin')#, bg = 'cyan2')
canvas_wl.create_window(0, 0, window=scrollable_frame_wl, anchor='nw')
# --- small frame (inside big frame so it doesn't hide scrollbars when window is smaller) ---
# external frame
#fram_s = tk.Frame(top_wl, width=300, height=300, bg='sky blue') # when it is directly in top_wl then it may hide scrollbars when window is smaller
fram_s = tk.Frame(fram_wl, width=300, height=300, bg='sky blue')
fram_s.place(x=0, y=100)
fram_s.pack_propagate(False) # don't resize external frame to canvas size
# inne canvas with size bigger then external frame
canvas_s = tk.Canvas(fram_s, width=900, height=900, bg='blue')
canvas_s.pack()
# inner frame with size bigger then frame (scrollbars will use this size to scroll it)
scrollable_frame_s = tk.Frame(canvas_s, width=900, height=900, bg='red')#, bg = 'cyan2')
canvas_s.create_window(0, 0, window=scrollable_frame_s, anchor='nw')
# ---
# add to inner frame in frame WL
label_wl_1 = tk.Label(scrollable_frame_wl, text='WL-Frame Top', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_wl_1.place(x=10, y=30)
label_wl_2 = tk.Label(scrollable_frame_wl, text='WL-Frame Bottom', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_wl_2.place(x=10, y=1500-30)
#label_wl_1.pack()
# add to inner frame in frame S
label_s_1 = tk.Label(scrollable_frame_s, text='S-Frame Top', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_s_1.place(x=10, y=30)
label_s_2 = tk.Label(scrollable_frame_s, text='S-Frame Bottom', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_s_2.place(x=10, y=900-30)
#label_s_1.pack()
# --- set scrollbars to move canvas ---
# to scroll vertically both canvas
def scroll_two_canvas(*args):
#print(args)
canvas_s.yview(*args)
canvas_wl.yview(*args)
scroll_bar_v.config(command=scroll_two_canvas) # it has to scroll two canvas
# to scroll horizontally one one canvas
scroll_bar_h.config(command=canvas_wl.xview) # it scroll only one canvas
# --- set canvas to resize scrollbars ---
# to update scrollbars when canvas is moved
canvas_wl.configure(xscrollcommand=scroll_bar_h.set)
canvas_wl.configure(yscrollcommand=scroll_bar_v.set)
# do't use it because it will conflict with previous
#canvas_s.configure(yscrollcommand=scroll_bar_v.set)
# get current size of element on canvas - if elements on canvas may change size (inner frame can change size) then you may need `bind('<Configure>')`
canvas_wl.configure(scrollregion=canvas_wl.bbox("all"))
canvas_s.configure(scrollregion=canvas_s.bbox("all"))
#def update_config(event):
# print(event)
# canvas_wl.configure(scrollregion=canvas_wl.bbox("all"))
# canvas_s.configure(scrollregion=canvas_s.bbox("all"))
#top_wl.bind('<Configure>', update_config)
# ---
top_wl.mainloop()
EDIT:
Code which resize smaller frame when bigger frame changes height. This way both frames shows the same percent of canvas height and scrollbar works better.
import tkinter as tk
top_wl = tk.Tk()
top_wl.title("Work Shop Layout")
# --- scrolls outside big frame (directly in window) ---
scroll_bar_v = tk.Scrollbar(top_wl, orient='vertical')
scroll_bar_v.pack(side='right', fill='y')
scroll_bar_h = tk.Scrollbar(top_wl, orient='horizontal')
scroll_bar_h.pack(side='bottom', fill='x')
# --- big frame (directly in window) ---
# external frame
fram_wl = tk.Frame(top_wl, width=500, height=500, bg='khaki1', relief='sunken')
fram_wl.pack(side='right', expand=True, fill='both')
fram_wl.pack_propagate(False) # don't resize external frame to canvas size
# inne canvas with size bigger then external frame
canvas_wl = tk.Canvas(fram_wl, width=1500, height=1500, bg='lime green')
canvas_wl.pack(fill='both')
# inner frame with size bigger then frame (scrollbars will use this size to scroll it)
scrollable_frame_wl = tk.Frame(canvas_wl, width=1500, height=1500, bg='moccasin')#, bg = 'cyan2')
canvas_wl.create_window(0, 0, window=scrollable_frame_wl, anchor='nw')
# --- small frame (inside big frame so it doesn't hide scrollbars when window is smaller) ---
# external frame
#fram_s = tk.Frame(top_wl, width=300, height=300, bg='sky blue') # when it is directly in top_wl then it may hide scrollbars when window is smaller
fram_s = tk.Frame(fram_wl, width=300, height=300, bg='sky blue')
fram_s.place(x=0, y=100)
fram_s.pack_propagate(False) # don't resize external frame to canvas size
# inne canvas with size bigger then external frame
canvas_s = tk.Canvas(fram_s, width=900, height=900, bg='blue')
canvas_s.pack()
# inner frame with size bigger then frame (scrollbars will use this size to scroll it)
scrollable_frame_s = tk.Frame(canvas_s, width=900, height=900, bg='red')#, bg = 'cyan2')
canvas_s.create_window(0, 0, window=scrollable_frame_s, anchor='nw')
# ---
# add to inner frame in frame WL
label_wl_1 = tk.Label(scrollable_frame_wl, text='WL-Frame Top', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_wl_1.place(x=10, y=30)
label_wl_2 = tk.Label(scrollable_frame_wl, text='WL-Frame Bottom', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_wl_2.place(x=10, y=1500-30)
#label_wl_1.pack()
# add to inner frame in frame S
label_s_1 = tk.Label(scrollable_frame_s, text='S-Frame Top', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_s_1.place(x=10, y=30)
label_s_2 = tk.Label(scrollable_frame_s, text='S-Frame Bottom', bd=2, width=30, bg='white', font=('Arial', 10, 'bold'), relief="solid")
label_s_2.place(x=10, y=900-30)
#label_s_1.pack()
# --- set scrollbars to move canvas ---
# to scroll both canvas
def scroll_two_canvas(*args):
#print(args)
canvas_s.yview(*args)
canvas_wl.yview(*args)
scroll_bar_v.config(command=scroll_two_canvas) # it has to scroll two canvas
scroll_bar_h.config(command=canvas_wl.xview) # it scroll only one canvas
# --- set canvas to resize scrollbars ---
# to update scrollbars when canvas is moved
canvas_wl.configure(xscrollcommand=scroll_bar_h.set)
canvas_wl.configure(yscrollcommand=scroll_bar_v.set)
# do't use it because it will conflict with previous
#canvas_s.configure(yscrollcommand=scroll_bar_v.set)
# get current size of element on canvas - if elements on canvas may change size (inner frame can change size) then you may need `bind('<Configure>')`
canvas_wl.configure(scrollregion=canvas_wl.bbox("all"))
canvas_s.configure(scrollregion=canvas_s.bbox("all"))
# --- resize smaller frame when bigger frame changes height
# to keep current height and compare with new height when resize, and to calculate scale used to resize smaller framer
frame_wl_height = fram_wl['height']
def update_config(event):
global frame_wl_height
if event.widget == fram_wl: #'.':
scale = event.height/frame_wl_height
frame_wl_height = event.height
fram_s['height'] = fram_s['height'] * scale
#canvas_wl.configure(scrollregion=canvas_wl.bbox("all"))
#canvas_s.configure(scrollregion=canvas_s.bbox("all"))
top_wl.bind('<Configure>', update_config)
# ---
top_wl.mainloop()
I am trying to make an application that displays a grid in the middle of the screen surrounded by two bars, a top bar and a bottom bar, which contain buttons for the user to press. These buttons should be able to display no matter where the user scrolls to on the grid and should not be cut off if the window is resized. I am struggling to configure the scrollbar to track the right area and to have the grid fall off the screen when the window is resized. Here is my code so far:
from tkinter import *
def add_row(event):
input_row = Entry(grid_frame, bd=1, text="", bg="white", relief="solid")
input_row.grid(row=grid_frame.rows, sticky=N+S+E+W)
Grid.rowconfigure(grid_frame, grid_frame.rows, weight=1)
grid_frame.rows = grid_frame.rows + 1
class GridFrame(Frame):
rows = 0
def __init__(self, root):
Frame.__init__(self, root, bd=1)
root = Tk(className="Main screen")
root.minsize(408, 80)
# size to quarter of screen
w, h = root.winfo_screenwidth() / 2, root.winfo_screenheight() / 2
root.geometry("%dx%d+0+0" % (w, h))
# grid_frame will resize and bars will not
Grid.rowconfigure(root, 1, weight=1)
Grid.columnconfigure(root, 0, weight=1)
myframe = Frame(root, bd=4, relief="groove")
myframe.grid(row=1, sticky=N + W + S + E)
canvas = Canvas(myframe)
grid_frame = GridFrame(canvas)
grid_frame.pack(fill=BOTH, expand=True)
grid_frame.bind("<Button-1>", add_row)
scrollbar = Scrollbar(myframe, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side=RIGHT, fill=Y)
canvas.pack(side=LEFT, fill=BOTH, expand=True)
topBar = Frame(root, grid_frame)
label = Label(topBar, text="Top Text")
label.pack()
topBar.grid(row=0, sticky=W+N+E+S)
bottomFrame = Frame(root, grid_frame)
label = Label(bottomFrame, text="Bottom Text")
label.pack()
bottomFrame.grid(row=2, sticky=E+S+W)
mainloop()
The scrollregion I want to track is the myframe/canvas/grid_frame combination I read to use from this post. The current functionality is that the scrollbar is never in an "active" state and rows added to the grid merely shrink the grid for it to fit within the display. To add a new row, click within the grid_frame region. Any help would be greatly appreciated! Here are some images of the current UI:
UI display with only a few rows
UI display with many more rows
There are two major problems with your code.
First, for the canvas to be able to scroll the inner frame, the inner frame must be a canvas object created with create_window. You're adding it to the canvas with pack, which means the canvas cannot scroll it.
To fix that, use create_window instead of pack:
canvas.create_window(0, 0, anchor="nw", window=grid_frame)
Second, you must reset the scrollregion attribute whenever the contents inside the canvas change. Normally this is done in a <Configure> event handler on the frame, but you can just as easily call it in your add_row function.
For example, add the following line to the end of add_row:
canvas.configure(scrollregion=canvas.bbox("all"))
With those two changes, the scrollbars will start to work as soon as the inner frame is taller than the canvas.
The above solves the problem of the inner window being able to scroll when you add items. In the specific example of this test program, you also have the problem that your binding is on the frame. At startup the frame has a size of 1x1 so it's a bit hard to click on. Moving the binding to the canvas will make this specific demo program work better.
This is the tkinter code I have used to create a scrollable region. I was wondering if there was a way I could add in a button which when you pressed it teleports you to a specific section of the scrollable region. Is it even possible?
self.canvas1 = Canvas(master, width=1200, height=700, scrollregion=(0,0,800,3000))
self.horizontalscroll = Scrollbar(master, orient=HORIZONTAL)
self.horizontalscroll.pack(side=BOTTOM, fill=X)
self.horizontalscroll.config(command=self.canvas1.xview)
self.verticalscroll = Scrollbar(master, orient=VERTICAL)
self.verticalscroll.pack(side=RIGHT, fill=Y)
self.verticalscroll.config(command=self.canvas1.yview)
self.canvas1.config(xscrollcommand=self.horizontalscroll.set, yscrollcommand=self.verticalscroll.set, bg="#4c5059")
self.canvas1.pack(fill=BOTH, expand=True)
self.frame1 = Frame(master)
self.frame1.configure(bg="#4c5059")
self.MainWindow = self.canvas1.create_window(10, 10, window=self.frame1, anchor='nw')
Yes, you can call the xview_moveto and yview_moveto methods to scroll anywhere you want. These take a value representing a percentage. Top/left is zero, bottom/right is 1.0, the middle is .5, etc.
For example, to show the bottom-right corner you could do this:
self.canvas1.xview_moveto(1.0)
self.canvas1.yview_moveto(1.0)
I have a Canvas inside a Frame in tkinter. The frame has a background color and the canvas too. But seemingly the frame background overrides the canvas color.
How can I increase the transparency of the frame background such that the canvas is visible?
import Tkinter
import tkMessageBox
from Tkinter import *
top = Tkinter.Tk()
frame = Frame(top, width=1000, height=1000, background="bisque")
frame.pack()
bottomframe = Frame(top, width=1000, height=1000, background="red")
bottomframe.pack( side = BOTTOM )
def creatLayers(no_of_layers, max_nodes_in_each_layer, frame1=bottomframe):
print 'here2'
listLayerRect=[]
listDelimiterRect=[]
#The canvas is created here.
mainCanvas=Tkinter.Canvas(frame1, bg="white", height=1000, width=1000)
frame1.pack(side=LEFT)
for i in range (0,no_of_layers):
print 'here3'
x=15*i
#rectangles that are being drawn on the canvas.
mainCanvas.create_polygon(x,0,x+10,0,x+10,1000,x,1000, outline='gray', fill='gray', width=2)
# listLayerRect.append(Tkinter.Canvas(frame1, bg="blue", height=1000, width=30))
# listDelimiterRect.append(Tkinter.Canvas(frame1, bg="yellow", height=1000, width=30))
L1 = Label(frame, text="Layers")
E1 = Entry(frame, bd =8)
L2 = Label(frame, text="Layers2")
def helloCallBack(E=E1,):
# tkMessageBox.showinfo( "Hello Python", "Hello World")
k=int(E.get())
print 'here'
print k
creatLayers(k,k)
B = Tkinter.Button(frame, text ="Enter", command = helloCallBack)
B.pack(side=LEFT)
#L1.pack(side=LEFT)
E1.pack(side=LEFT)
#L2.pack(side=LEFT)
top.mainloop()
So, basically, when you enter a number in the box and press Enter, a canvas gets created in the red part (frame) and a grid pattern should be drawn on that canvas. Essentially, there are 2 frames, the top frame contains the button and the entry box, the lower frame should be able to draw stuff inside on the canvas created within.
The reason why the canvas is not displayed is because you're not telling it to be displayed inside frame1, i.e. you forgot to pack (or grid, or place) it, so just do in the meantime:
...
mainCanvas=Tkinter.Canvas(frame1, bg="white", height=1000, width=1000)
mainCanvas.pack()
...
Now depending on what you really want to achieve from the layout point of view, you may need to think better how to use pack, grid and pack.
Here's the result after the correction above (on Mac OS X, Sierra)
Before clicking Enter
After clicking Enter
In general, just remember that a frame will have a empty body if it doesn't contain any widget with a certain specified size.
I am trying to make a frame scrollable, and the only way I found to do this is making a scrollable canvas and adding a frame to it. This would work fine, if it worked for me.
I am able to create a scrollable canvas that works fine, but I can't seem to properly add a frame inside of it:
self.title = Label(root, text="Brnr", font=("Helvetica", 50), anchor = W, pady = 40, padx = 50)
self.title.pack (anchor = NW)
#creates title widget for title
self.frame = Frame(screen, bd =1)
self.frame.pack(fill = BOTH)
#Creates frame widget under which all other widgets will be kept
self.canvas = Canvas(self.frame, bd=1,scrollregion=(0,0, 1000, 1000), height = 600)
#creates canvas so that screen can be scrollable
self.scrollbar = Scrollbar(self.frame, command=self.canvas.yview)
#creates scrollbar
self.canvas.config(yscrollcommand=self.scrollbar.set)
#connects the scrollbar to the canvas
self.scrollbar.pack(side=RIGHT, fill=Y)
self.canvas.pack(expand=YES, fill=BOTH)
#packs the scrollbar and canvas so that they fill the remainder of the screen
self.frameC = Frame(bg = "red")
self.canvas.create_window(0,0, anchor = NW, window = self.frameC, width = 200, height = 200)
#creates window on the scrollable area to add other widgets
self.frameC.pack()
self.groupRec = LabelFrame(self.frameC, text ="Recommendations:", font=("Helvetica", 20))
self.groupRec.pack()
self.signupButton = Button(self.groupRec, text="Sign Up", width=10)
self.signupButton.pack(side=RIGHT)
#creates button to submit login
This gives me a scrollable, but empty, canvas, with none of the labelframe/button appearing.
By default, when you add a window to a canvas, the center of the window will be at the coordinates you give. Thus, the center of your frame will be at 0,0 which is the upper-left corner of the canvas. You can't see the widgets because they are outside the borders of the canvas.
The solution is to include anchor="nw" in the call to create_window, which will place the upper-left corner of your frame in the upper left corner of your canvas.
Don't forget to set the scroll region of the canvas to match the size of your frame. The easiest way to do that is with the command self.canvas.config(scrollregion=self.canvas.bbox("all")). You'll probably also need to add a binding to <Configure> on the canvas so that you can resize the inner frame when the user resizes the window. That's not always necessary, it depends a bit on exactly what you are trying to accomplish.
Here's a pro tip: to debug problems like this it's really helpful to temporarily give your frame and canvas different colors to more easily visualize what is happening.
Don't re-invent the wheel. Install Pmw (Python meta-widgets), assuming you are using Tkinter, http://pmw.sourceforge.net/ and use Pmw.ScrolledFrame.