2

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()
stovfl
  • 14,998
  • 7
  • 24
  • 51
葉泓緯
  • 23
  • 3
  • Have you tried making the canvas bigger? You're forcing it to be a small size, and/or use options to `pack` to cause the canvas and frames to fill the space given to them? – Bryan Oakley Sep 30 '19 at 12:57

1 Answers1

0

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'.

enter image description here


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

stovfl
  • 14,998
  • 7
  • 24
  • 51
  • Hi stovfl, Thank you for making such a wonderful answer! Sorry, I'm a beginner in python. I got a new problem after I change the function "myfunction" to "on_configure". – 葉泓緯 Oct 08 '19 at 02:57
  • The error message like below. "fm_grid.bind("",on_configure)" Exception in Tkinter callback Traceback (most recent call last): File "D:\anaconda\lib\tkinter\__init__.py", line 1705, in __call__ return self.func(*args) TypeError: on_configure() missing 1 required positional argument: 'event' – 葉泓緯 Oct 08 '19 at 03:05
  • ***`TypeError: on_configure() missing 1 required positional argument: 'event' `***: I'm using `OOP`, therefore `def on_configure(self, event):` is a `class methode` which requires `self` as the first parameter. You are using functional programming, remove `self` and you will be fine. – stovfl Oct 08 '19 at 06:43