Okay, so I have sort of a unique situation here, so bear with me. I want to be able to create so called "barriers" around the viewing area (the part of the canvas visible to the user) of a tkinter canvas object. For example, take a look at the screenshots below (based on the MCVE at the end):
As you can see in the image above, the line currently goes outside the viewing area of the canvas when the user reaches the end. However, that is not what I want. Instead, I want that whenever the user reaches the end of the canvas's visible area, a "barrier" gets hot, and upon contact, a carriage return occurs, and the line(s) continue(s) on from there. So instead of the above, what I really want is this:
Here is the MCVE I used to take the above screenshots:
import tkinter as TK
xold = None
yold = None
class canvas(TK.Frame):
def __init__(self, root, *args, **kwargs):
# Initialize a tkinter frame widget
TK.Frame.__init__(self, root, width = 800, height = 850, *args, **kwargs)
self.root = self.winfo_toplevel()
self.bg = "white"
self.width, self.height = 850, 800
self.canvwidth, self.canvheight = 10000, 10000
# Set up the canvas and its corresponding scrollbars
self.canvas = TK.Canvas(root, width=850, height=800,
bg=self.bg, borderwidth=0, highlightthickness = 5, highlightbackground = 'brown', highlightcolor = 'brown')
self.hscroll = TK.Scrollbar(root, command=self.canvas.xview,
orient=TK.HORIZONTAL)
self.vscroll = TK.Scrollbar(root, command=self.canvas.yview)
self.canvas.configure(xscrollcommand=self.hscroll.set,
yscrollcommand=self.vscroll.set)
self.rowconfigure(0, weight=1, minsize=0)
self.columnconfigure(0, weight=1, minsize=0)
# Add the scrollbars into the root window
self.canvas.grid(padx=1, pady=1, row=0,
column=0, rowspan=1, columnspan=1, sticky = 'news')
self.vscroll.grid(padx=1, pady=1, row=0,
column=1, rowspan=1, columnspan=1, sticky='news')
self.hscroll.grid(padx=1, pady=1, row=1,
column=0, rowspan=1, columnspan=1, sticky='news')
# Call the `reset` method of the canvas class
self.reset()
# Bind the `line` method to the 'l' key of the users keyboard (as an example of what I want)
self.root.bind('<l>', self.line)
def reset(self, canvwidth=None, canvheight=None, bg = None):
###############################################################################################################################
# This adds the scrollbars themselves to the canvas and adapts them to the canvas's size (in this case, 10000 x 10000 pixels) #
###############################################################################################################################
if canvwidth:
self.canvwidth = canvwidth
if canvheight:
self.canvheight = canvheight
if bg:
self.bg = bg
self.canvas.config(bg=bg,
scrollregion=(-self.canvwidth//2, -self.canvheight//2,
self.canvwidth//2, self.canvheight//2))
self.canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) /
self.canvwidth)
self.canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) /
self.canvheight)
def line(self, event):
########################################################################################################
# Create a short, horizontal, black line on every press of the user's 'l' key (as an example to go by) #
########################################################################################################
global xold, yold
if xold != None and yold != None:
pass
else:
xold, yold = 0, 0
self.canvas.create_line(xold, yold, xold+30, yold, smooth = TK.TRUE, width = 1, capstyle = TK.ROUND, joinstyle = TK.ROUND, fill = 'black')
xold = xold+30
yold = yold
if __name__ == '__main__':
# Create a window, and provide that window to the canvas class as the root window
root = TK.Tk()
root.geometry('900x850')
canvas(root)
root.mainloop()
Is it possible to add this ability to the MCVE above using tkinter? If so, how would I get started on trying to implement it?
I am not sure what you are actually trying to do (especially trying to constrain drawing in the displayed region while you provide a very large canvas with scrollbars).
For the simplest case, all you need is a bound value and to test xold against it
if xold > 440:
xold = -410
yold += 30
If you want to take into account the current displayed area, you have to combine information from canvas scrollregion and xview methods. The first return the bounds of the canvas and former the relative position of the displayed aera in the scrollregion.
scroll = list(map(int,self.canvas["scrollregion"].split()))
xview = self.canvas.xview()
leftbound = scroll[0] + xview[1] * (scroll[2]-scroll[0])
if xold > leftbound:
rightbound = scroll[0] + xview[0] * (scroll[2]-scroll[0])
xold = rightbound
yold += 30
Related
I'm trying to generate an arbitrary number of rows, where each row contains a label in the left side and a scrolled canvas on the right. My code is as follows:
import tkinter as tk
class App(tk.Frame):
def __init__(self, root):
super(App, self).__init__(root)
self.root = root
# CREATE OUTER FRAMES #
self.main_frame = MainFrame(root)
self.main_frame.pack(padx=0, pady=0)
self.RowCollection = RowCollection(root, self.main_frame)
self.row_1 = self.RowCollection.row()
self.row_2 = self.RowCollection.row()
self.row_3 = self.RowCollection.row()
def scroll_x(*args):
# link scrollbar to canvas' xviex
self.row_1.canvas.xview(*args)
self.row_2.canvas.xview(*args)
self.row_3.canvas.xview(*args)
self.scrollbar = tk.Scrollbar(self.RowCollection.right_frame, orient=tk.HORIZONTAL)
self.scrollbar.config(command=scroll_x)
self.scrollbar.grid(row=Row.row_count, column=0, sticky='ew')
canvas_1 = self.row_1.canvas
canvas_1.config(xscrollcommand=self.scrollbar.set)
canvas_1.bind('<Configure>', lambda event: canvas_1.configure(scrollregion=canvas_1.bbox('all')))
canvas_2 = self.row_2.canvas
canvas_2.config(xscrollcommand=self.scrollbar.set)
canvas_2.bind('<Configure>', lambda event: canvas_2.configure(scrollregion=canvas_2.bbox('all')))
canvas_3 = self.row_3.canvas
canvas_3.config(xscrollcommand=self.scrollbar.set)
canvas_3.bind('<Configure>', lambda event: canvas_3.configure(scrollregion=canvas_3.bbox('all')))
# WINDOW_WIDTH = 1000
WINDOW_HEIGHT = 700
WINDOW_WIDTH = root.winfo_screenwidth() - 30
root.geometry(f'{WINDOW_WIDTH}x{WINDOW_HEIGHT}+0+0')
self.pack(side="top")
class MainFrame(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master, bg='blue')
class RowCollection:
"""Collection of rows"""
def __init__(self, root, frame):
self.row_list = []
self.root = root
self.frame = frame
self.right_frame = tk.Frame(self.frame, bg='red', width=10000)
self.right_frame.pack(side=tk.RIGHT)
self.left_frame = tk.Frame(self.frame)
self.left_frame.pack(side=tk.LEFT, fill=tk.Y)
def row(self):
row = Row(self)
self.row_list.append(row)
return row
class Row:
"""Every row consists of a label on the left side and a canvas on the right side"""
row_count = 0
label_width = 15
line_weight = 3
line_yoffset = 3
padx = 20
def __init__(self, collection):
self.frame = collection.frame
self.root = collection.root
self.collection = collection
self.canvas = None
self.label = None
self.text = f'Canvas {Row.row_count}'
self.height = 100
self.root.update()
self.label = tk.Label(self.collection.left_frame,
text=self.text,
height=1,
width=Row.label_width,
relief='raised')
self.label.grid(row=Row.row_count, column=0, sticky='ns')
# configure row size to match future canvas height
self.collection.left_frame.grid_rowconfigure(Row.row_count, minsize=self.height)
self.root.update()
self.canvas = tk.Canvas(self.collection.right_frame,
width=self.root.winfo_width() - self.label.winfo_width(),
height=self.height,
bg='white',
highlightthickness=0)
self.canvas.grid(row=Row.row_count, column=0, sticky='ew')
self.root.update()
# draw line
self.line = self.canvas.create_rectangle(self.padx,
self.canvas.winfo_height() - Row.line_yoffset,
self.canvas.winfo_width() - self.padx,
self.canvas.winfo_height() - Row.line_yoffset + Row.line_weight,
fill='#000000', width=0, tags='line')
# Create point at canvas edge to prevent scrolling from removing padding
self.bounding_point = self.canvas.create_rectangle(0, 0, 0, 0, width=0)
self.bounding_point = self.canvas.create_rectangle(self.canvas.winfo_width(), self.canvas.winfo_width(),
self.canvas.winfo_width(), self.canvas.winfo_width(),
width=0)
# set column weight to 1 so it expands
self.collection.right_frame.grid_columnconfigure(0, weight=1)
# config canvas
self.canvas.config(scrollregion=self.canvas.bbox('all'))
Row.row_count += 1
if __name__ == '__main__':
root = tk.Tk()
app = App(root)
root.mainloop()
I have created a main frame, with a left and a right frame as children. The left frame contains the labels and the right frame contains the canvases.
I need to have the option to create very wide canvases, but increasing the width option of the right frame seems to have no effect on the canvas size after some value around 3000. What is going on?
Additionally, I would like to have the labels always visible, but, currently, increasing the canvases size eventually pushes the labels outside visibility.
I need to have the option to create very wide canvases, but increasing the width option of the right frame seems to have no effect on the canvas size after some value around 3000. What is going on?
You are adding the canvas to a frame with grid. By default, the frame will grow or shrink to fit the canvas. Therefore, no matter how large you make the frame, it will shrink down to be roughly the size of the canvas (and/or the size of the space in the frames parent, depending on how you added it to the parent).
If you want the canvas to be a specific size, the best thing is to give the canvas that size rather than relying on the width of its containing widget.
If you genuinely want to control the size of the canvas by controlling the size of the frame, you can tell the frame to not grow or shrink to fit its children. You do this by turning geometry propagation off. This tells the frame to ignore the size of its children. This is rarely the right solution since it often requires more work on your part to make a responsive GUI. Nevertheless, to turn geometry propagation off do this:
self.collection.right_frame.grid_propagate(False)
I'm trying to control multiple canvases widths with the mouse wheel. What I have so far is this:
import tkinter as tk
class App(tk.Frame):
row_amount = 3
def __init__(self, root):
super(App, self).__init__(root)
self.root = root
self.main_frame = tk.Frame(root)
self.main_frame.pack(expand=True, fill=tk.BOTH)
self.row_collection = RowCollection(root, self.main_frame)
for i in range(App.row_amount): self.row_collection.row()
window_height = App.row_amount * 100
window_width = root.winfo_screenwidth() - 30
root.geometry(f'{window_width}x{window_height}+0+0')
self.row_collection.right_frame.grid_columnconfigure(0, weight=1)
self.row_collection.left_frame.grid_columnconfigure(0, weight=1)
self.pack()
class RowCollection:
"""Collection of rows"""
def __init__(self, root, frame):
self.row_list = []
self.root = root
self.frame = frame
self.right_frame = tk.Frame(self.frame, bg='red')
self.right_frame.pack(side=tk.RIGHT, expand=tk.YES, fill=tk.BOTH)
self.left_frame = tk.Frame(self.frame)
self.left_frame.pack(side=tk.LEFT, fill=tk.Y)
self.scrollbar = tk.Scrollbar(self.right_frame, orient=tk.HORIZONTAL)
self.scrollbar.config(command=self.scroll_x)
def row(self):
row = Row(self)
self.row_list.append(row)
return row
def scroll_x(self, *args):
for row in self.row_list:
row.canvas.xview(*args)
def zoomer(self, event=None):
print('zooming')
for row in self.row_list:
scale_factor = 0.1
curr_width = row.canvas.winfo_reqwidth()
print(f'event delta={event.delta}')
if event.delta > 0:
row.canvas.config(width=curr_width * (1 + scale_factor))
elif event.delta < 0:
row.canvas.config(width=curr_width * (1 - scale_factor))
row.canvas.configure(scrollregion=row.canvas.bbox('all'))
class Row:
"""Every row consists of a label on the left side and a canvas with a line on the right side"""
row_count = 0
label_width = 15
line_weight = 3
line_yoffset = 3
padx = 20
def __init__(self, collection):
self.frame = collection.frame
self.root = collection.root
self.collection = collection
self.canvas = None
self.label = None
self.text = f'Canvas {Row.row_count}'
self.height = 100
self.root.update()
self.label = tk.Label(self.collection.left_frame,
text=self.text,
height=1,
width=Row.label_width,
relief='raised')
self.label.grid(row=Row.row_count, column=0, sticky='ns')
# configure row size to match future canvas height
self.collection.left_frame.grid_rowconfigure(Row.row_count, minsize=self.height)
self.root.update()
self.canvas = tk.Canvas(self.collection.right_frame,
width=10000,
height=self.height,
bg='white',
highlightthickness=0)
self.canvas.grid(row=Row.row_count, column=0, sticky=tk.W)
self.root.update()
# draw line
self.line = self.canvas.create_rectangle(self.padx,
self.canvas.winfo_height() - Row.line_yoffset,
self.canvas.winfo_width() - self.padx,
self.canvas.winfo_height() - Row.line_yoffset + Row.line_weight,
fill='#000000', width=0, tags='line')
# config canvas
self.canvas.config(scrollregion=self.canvas.bbox('all'))
self.canvas.config(xscrollcommand=self.collection.scrollbar.set)
self.canvas.bind('<Configure>', lambda event: self.canvas.configure(scrollregion=self.canvas.bbox('all')))
self.canvas.bind('<MouseWheel>', self.collection.zoomer)
# Create point at canvas edge to prevent scrolling from removing padding
self.bounding_point = self.canvas.create_rectangle(0, 0, 0, 0, width=0)
self.bounding_point = self.canvas.create_rectangle(self.canvas.winfo_width(), self.canvas.winfo_width(),
self.canvas.winfo_width(), self.canvas.winfo_width(),
width=0)
Row.row_count += 1
self.collection.scrollbar.grid(row=Row.row_count, column=0, sticky='ew')
if __name__ == '__main__':
root = tk.Tk()
app = App(root)
root.mainloop()
The canvases themselves are inside right_frame, and the number of canvases is given by row_amount. The left_frame contains labels for each of the canvases. The canvases should be allowed to be pretty wide, so I initially set a width value of 10000. Because of that, they start partially visible, with the rest being accessible via a scrollbar.
What I would like is for the mouse wheel to control the size of the canvas as a whole (that is, both what is currently visible and what could be viewed using the scrollbar), similar to what would happen in an audio or video editing software timeline.
Right now, when I use the mouse wheel, what seems to get resized is not the whole canvas, but only the 'visible' portion. Resize it to be small enough and you can start to see it's frame background on the right portion of the window.
What am I missing here?
What am I missing here?
I think what you're missing is that the drawable area of the canvas is not at all related to the physical size of the canvas widget. You do not need to resize the canvas once it has been created. You can draw well past the borders of the widget.
If you want to be able to scroll elements into view that are not part of the visible canvas, you must configure the scrollregion to define the area of the virtual canvas that should be visible.
You said in a comment you're trying to create a timeline. Here's an example of a canvas widget that "grows" by adding a tickmark every second. Notice that the canvas is only 500,100, but the drawable area gets extended every second.
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, width=500, height=100, bg="black")
vsb = tk.Scrollbar(root, orient="vertical", command=canvas.yview)
hsb = tk.Scrollbar(root, orient="horizontal", command=canvas.xview)
canvas.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
canvas.grid(row=0, column=0, sticky="nsew")
vsb.grid(row=0, column=1, sticky="ns")
hsb.grid(row=1, column=0, sticky="ew")
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
counter = 0
def add_tick():
global counter
# get the current state of the scrollbar. We'll use this later
# to determine if we should auto-scroll
xview = canvas.xview()
# draw a new tickmark
counter += 1
x = counter * 50
canvas.create_text(x, 52, anchor="n", text=counter, fill="white")
canvas.create_line(x, 40, x, 50, width=3, fill="red")
# update the scrollable region to include the new tickmark
canvas.configure(scrollregion=canvas.bbox("all"))
# autoscroll, only if the widget was already scrolled
# as far to the right as possible
if int(xview[1]) == 1:
canvas.xview_moveto(1.0)
canvas.after(1000, add_tick)
add_tick()
root.mainloop()
In my orignal code I'm doing something like below, just with much more math.
It works but I dont like that I can see how the thumb of my scrollbar (hscrbar) is moveing from position 0 to 1 while I calculate the width in get_width(self): of my rectangel.
Cause in my original code I need to see it everytime I add something.
At the moment I havent an idea to solve this and you may are aware of a solution for it.
import tkinter as tk
root = tk.Tk()
class my_figure(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.root = self.winfo_toplevel()
# DownFrame
self.button = tk.Button(self, text='add', command=self.add)
self.button.grid(column=0, row=0)
self.body = tk.Frame(self, relief='sunken')
self.hscrbar = tk.Scrollbar(self.body, orient=tk.HORIZONTAL)
self.vscrbar = tk.Scrollbar(self.body)
self.Display = tk.Canvas(self.body,
xscrollcommand=self.hscrbar.set,
yscrollcommand=self.vscrbar.set)
self.hscrbar.config(command=self.Display.xview)
self.body.grid(column=0, row=1, sticky='nswe')
self.vscrbar.grid(column=1,sticky='ns')
self.hscrbar.grid(row=1, column=0, sticky='we')
self.Display.grid(column=0, row=0,
sticky='nswe')
self.vscrbar.config(command=self.Display.yview)
self.hscrbar.config(command=self.Display.xview)
self.x = tk.IntVar()
self.y = tk.IntVar()
self.x.set(10)
self.y.set(10)
self.height = 10
def add(self):
self.Display.create_rectangle(self.x.get(),self.y.get(),self.get_width(),self.height)
self.old_x = self.x.get()
self.old_y = self.y.get()
self.x.set(self.old_x+40)
self.y.set(self.old_y+20)
self.Display.config(scrollregion=self.Display.bbox("all"))
def get_width(self):
if self.hscrbar.get()[0] == 0 and self.hscrbar.get()[1] == 1: #if scrollbar shows everything
return self.Display.winfo_width()#return width of the canvas
else:
self.Display.xview_moveto(0) #scrollbar at postition 0
self.root.update_idletasks() #update idletasks to get correct value
value = self.Display.winfo_width()-round(self.Display.winfo_width()*self.hscrbar.get()[1])
width = value+self.Display.winfo_width() #calculate the width
self.Display.xview_moveto(1) #move to position 1 to show my the end of rectangel
return width
figure = my_figure(root)
figure.grid()
root.mainloop()
My guess is that the problem is largely due to the fact that inside of get_width you're moving the scrollbar, calling update_idletasks, and then moving the scrollbar again. That call to update_idletasks causes the window to redraw. That redraw means you'll see the scrollbar move to the left, and then it will move back to the right when the function is finished.
It's not entirely clear what get_width is supposed to do, but I'm guessing you can remove all of that code and replace it with self.Display.bbox("all"), and then grabbing the x coordinates from the result to compute the width of the drawing.
My objective is to solve the problem of the grid exceeding the window(shown as figure.1)
enter image description here
My program function is creating a grid that number of columns defined by user.
I tried using canvas to solve this problem, but it still doesn't work successfully.
It doesn't show the full grid in the canvas.(shown as figure.2)
enter image description here
Below is my code, could you please help solve the problems or give me some advice.
Thanks a lot.
Code:
import tkinter as tk
import tkinter.messagebox
import tkinter.filedialog
MainWindow = tk.Tk()
MainWindow.title('Helloworld')
MainWindow.geometry('1000x800')
def btn_generate():
global EntryNamelist
global Entrycoordinatelist
global EntryLabellist
con_num = en_condition_num.get()
if con_num != '':
#### Grid Body
for i in range(1,int(con_num) +1 ):
lb_name = tk.Label(fm_grid, text="Condition" + str(i) )
lb_name.grid(row=i, column=0, padx=2, pady=1, ipadx=20, ipady=5)
En_name = tk.Entry(fm_grid, bd = 2,width = 10,font=('Ubuntu', 10))
En_name.grid(row=i, column=1, padx=2, pady=1, ipadx=35, ipady=5)
En_coor = tk.Entry(fm_grid, bd = 2,width = 10,font=('Ubuntu', 10))
En_coor.grid(row=i, column=2, padx=2, pady=1, ipadx=200, ipady=5)
else:
tk.messagebox.showerror("Error", "Please input a num of conditions")
fm_main = tk.Frame()
fm3 = tk.Frame(fm_main)
lb_condition = tk.Label(fm3,text = 'Please input the number of condition')
lb_condition.pack(side="left")
en_condition_num = tk.Entry(fm3, bd = 2,width = 5)
en_condition_num.pack()
fm3.pack()
btn_generate = tk.Button(fm_main,text="Generate Grid",command=btn_generate)
btn_generate.pack()
lb_en = tk.Label(fm_main,text = '')
lb_en.pack()
def myfunction(event):
canvas.configure(scrollregion=canvas.bbox("all"),width=200,height=200)
canvas=tk.Canvas(fm_main)
fm_grid = tk.Frame(canvas)
fm_grid.pack()
myscrollbar=tk.Scrollbar(fm_main,orient="vertical",command=canvas.yview)
canvas.configure(yscrollcommand=myscrollbar.set)
myscrollbar.pack(side="right",fill="y")
canvas.pack(side="left")
canvas.create_window((4,4),window=fm_grid,anchor='nw')
fm_grid.bind("<Configure>",myfunction)
fm_main.pack()
MainWindow.mainloop()
Question: It doesn't show the full grid in the canvas
You have to sync, the width of the Canvas with the width of the Frame inside.
Note: fm_grid = tk.Frame(canvas, bg='blue') is shown in 'blue'.
Dont's:
Remove fm_grid.pack(), you layout with: canvas.create_window(....
Also, i recommend not to use a offset (4, 4), because you have to calculate with this offset on every canvas.configure(..., width=width + 4. Use (0, 0) instead.
# fm_grid.pack()
...
canvas.create_window((4,4),window=fm_grid,anchor='nw')
Useless, to create dynamically window:
Your usage of canvas.bbox is useless, because it's the dimension you want to layout this widget.
Using a fixed width=200, smaller than fm_grid.width will allways cut the fm_grid content, it's not dynamically either.
canvas.configure(scrollregion=canvas.bbox("all"),width=200,height=200)
How to sync the width of the Canvas with the width of the Frame inside?
You bound fm_grid.bind("<Configure>", therefore the event.widget is fm_grid, the Frame inside.
Get the dimensions of the event.widget from there w.winfo_... and build a bbox tuple to set scrollregion.
Use width to set canvas.width, to be in sync with the event.widget.winfo_width().
class ScrollCanvas(tk.Canvas):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
def create_window(self, child):
super().create_window((0, 0), window=child, anchor='nw')
child.bind("<Configure>", self.on_configure)
def on_configure(self, event):
w = event.widget
bbox = x, y, width, height = 0, 0, w.winfo_width(), w.winfo_height()
self.configure(scrollregion=bbox, width=width)
Tested with Python: 3.5 - 'TclVersion': 8.6 'TkVersion': 8.6
The following code creates a canvas with a frame inside. I am using the canvas to enable the addition of a vertical scrollbar, which appears as necessary. I have coloured the various sections - root, blue; window, green; canvas, red and frame, cyan. From this it can be seen that window completely fills root (no blue seen). However, canvas only fills window in the vertical direction (green visible to the rhs on expanding the window, and frame only fills canvas in the horizontal direction (red visible below cyan). I can't see what is causing the problem. Frame is bound to canvas and canvas has sticky='nsew' set so should be filling window.
import Tkinter as tk
import tkFileDialog
class AutoScrollbar(tk.Scrollbar):
# a scrollbar that hides itself if it's not needed. only
# works if you use the grid geometry manager.
def set(self, lo, hi):
if float(lo) <= 0.0 and float(hi) >= 1.0:
# grid_remove is currently missing from Tkinter!
self.tk.call("grid", "remove", self)
else:
self.grid()
tk.Scrollbar.set(self, lo, hi)
def pack(self, **kw):
raise TclError, "cannot use pack with this widget"
def place(self, **kw):
raise TclError, "cannot use place with this widget"
class Window(tk.Frame):
def UserInput(self,status,name):
row = self.row
optionLabel = tk.Label(self.frame)
optionLabel["text"] = name
optionLabel.grid(row=row, column=0, sticky='w')
var = tk.StringVar(root)
var.set(status)
w = tk.Entry(self.frame, textvariable= var)
w.grid(row=row, column=1, sticky='ew')
self.grid_columnconfigure(1,weight=1)
self.row += 1
return w
def on_canvas_resize(self,event):
padding = 8
width = self.canvas.winfo_width() - padding
self.canvas.itemconfigure("frame", width=width)
def OnFrameConfigure(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def __init__(self,parent):
tk.Frame.__init__(self,parent)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0,weight=1)
self.vscrollbar = AutoScrollbar(self,orient = tk.VERTICAL)
self.vscrollbar.grid(row = 0,column = 3, sticky = 'ns')
self.canvas = tk.Canvas(self, yscrollcommand = self.vscrollbar.set,bg='red')
self.canvas.grid(row=0, column=0, sticky='nsew')
self.canvas.grid_rowconfigure(0, weight = 1)
self.canvas.grid_columnconfigure(0, weight = 1)
self.frame = tk.Frame(self.canvas, bg='cyan')
self.frame.bind("<Configure>", self.OnFrameConfigure)
self.frame.grid_rowconfigure(0, weight=1)
self.frame.grid_columnconfigure(0,weight=1)
self.frame.grid_columnconfigure(1,weight=3)
self.vscrollbar.config(command=self.canvas.yview)
self.canvas.create_window(0, 0, anchor = tk.NW, window= self.frame, tags =["frame"])
self.canvas.bind("<Configure>", self.on_canvas_resize)
self.row = 0
for i in range(10):
self.Number = self.UserInput("1", "number")
if __name__ == "__main__":
root = tk.Tk()
root.configure(background='blue')
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(0,weight=1)
window = Window(root)
window.configure(background='green')
window.grid(row=0,column=0,sticky='nsew')
root.mainloop()
You are putting the canvas in column 0 (zero), and you are properly giving that column a weight. However, you are also giving an equal weight to column 1 (one) (see line 29), which prevents the canvas from filling the window. Remove line 29, which is setting the weight of column 1 to 1.
Your frame fills the canvas horizontally because that is what you are telling it to do. If you want it to also be the same height you will need to set its height, too.