0

I am writing a simple spreadsheet viewer. What I want is for row 0 to be controls and not scroll, Row 1 to be column headers and scroll horizontally with the data, rows 2 through end -1 to be data and scroll scroll both horizontally and vertically and a scroll bar at the far right, and the end row to be the horizontal scroll bar. I want the program to fill the entire window. What I have now is only the top left quarter of the window is used, horizontal scrolling works but there is no data and no vertical scroll bar.


import tkinter as tk
from tkinter import messagebox

def NewFrame(parent,d,ro = 0, co = 0, st = "NEWS", w = 1):
    fr = tk.Frame(parent, **d)
    fr.grid(row=ro,column=co, sticky=st)
    fr.grid_columnconfigure(co, weight=w)
    fr.grid_rowconfigure(ro, weight=w)
    return fr

def NewCanvas(parent,d,ro = 0, co = 0, st = "NEWS", w = 1):
    ca = tk.Canvas(parent, **d)
    ca.grid(row=ro,column=co, sticky=st)
    ca.grid_columnconfigure(co, weight=w)
    ca.grid_rowconfigure(ro, weight=w)
    return ca

class test:
    def __init__(self,root,hxw):
        self.hxw=hxw
        root.wm_title("Test")
        root.configure(bd=2)
        root.configure(background="black")
        root.grid_columnconfigure(0, weight=1)
        root.grid_rowconfigure(0, weight=1)
        root.grid_rowconfigure(1, weight=1)
        screen_width = root.winfo_screenwidth()
        screen_height = root.winfo_screenheight()
        if not self.hxw:
            self.hxw='{0:d}x{1:d}+{2:d}+{3:d}'.format(min(940,screen_width),min(560,screen_height),int(screen_width/3),int(screen_height/3))
        root.geometry(self.hxw) #Width x Height
        root.update()
        self.top = root
        self.topFrame = NewFrame(self.top, {'height':25, 'background':"blue"}, st="NEW") 
        tk.Button(self.topFrame, text="Help", command=self.callback4).grid(row=0, column=0, sticky='E')
        self.hsFrame = NewFrame(self.top, {'background':"red"}, st="NEW", ro=1)
        self.hcanvas = NewCanvas(self.hsFrame, {'background':"green"}, st="NEW")
        self.mFrame = NewFrame(self.hcanvas,{})

        self.mFrame.bind(
            "<Configure>",
            lambda e: self.hcanvas.configure(
                scrollregion=self.hcanvas.bbox("all")
            )
        )

        self.hcanvas.create_window((0, 0), window=self.mFrame, anchor="nw")

        # Create a horizontal scrollbar linked to the canvas.
        hsbar = tk.Scrollbar(self.hsFrame, orient=tk.HORIZONTAL, command=self.hcanvas.xview)
        hsbar.grid(row=2, column=0, sticky=tk.EW)
        self.hcanvas.configure(xscrollcommand=hsbar.set)

        self.labelFrame = NewFrame(self.mFrame, {'background':"grey", 'height':25})                    #place a frame on the canvas, this frame will hold the child widgets 
        for col in range(20):
            tkl = tk.Label(self.labelFrame, width=20, borderwidth="1", relief="solid", text=("col %d" % (col+1)))
            tkl.grid(row=0, column=col, sticky='news')
            self.labelFrame.grid_columnconfigure(col,weight=1)

        self.vsFrame = NewFrame(self.mFrame, {'background':"yellow"}) 
        self.vcanvas = NewCanvas(self.vsFrame, {'background':"#ff00ff"})

        self.botFrame = NewFrame(self.vcanvas, {'background':"orange"})                   #place a frame on the canvas, this frame will hold the child widgets 

        self.botFrame.bind(
            "<Configure>",
            lambda e: self.vcanvas.configure(
                scrollregion=self.vcanvas.bbox("all")
            )
        )

        self.vcanvas.create_window((0, 0), window=self.vsFrame, anchor="nw")

##        # Create a vertical scrollbar linked to the canvas.
        self.vsbar = tk.Scrollbar(self.vsFrame, orient=tk.VERTICAL, command=self.vcanvas.yview)
        self.vsbar.grid(row=0, column=1, sticky=tk.NS)
        self.vcanvas.configure(yscrollcommand=self.vsbar.set)
#
# Add 50-by-20 labels to the frame
        rows = 50
        columns = 20
        for i in range(0, rows):
            for j in range(0, columns):
                tkl = tk.Label(self.botFrame, width=10, borderwidth="1", relief="solid", text=("%d,%d" % (i+1, j+1)))
                tkl.grid(row=i, column=j, sticky='news')
                self.botFrame.grid_columnconfigure(j, weight=1)

    def callback4(self):
        print('root', root.winfo_geometry())
        print('self.topFrame', self.topFrame.winfo_geometry())
        print('self.hsFrame', self.hsFrame.winfo_geometry())
        print('self.hcanvas', self.hcanvas.winfo_geometry())
        print('self.mFrame', self.mFrame.winfo_geometry())
        print('self.labelFrame', self.labelFrame.winfo_geometry())
        print('self.vsFrame', self.vsFrame.winfo_geometry())
        print('self.vcanvas', self.vcanvas.winfo_geometry())
        print('self.botFrame', self.botFrame.winfo_geometry())
        print('------------------------------')
        messagebox.showinfo(
            "help",
            "This is help"
            )


# Launch the GUI
root = tk.Tk()
test(root,None)
root.mainloop()

                
  • Your problem is that you haven't applied `rowconfigure` or `columnconfigure` to `root` – Derek Jul 29 '21 at 08:35
  • You shouldn't call `.grid` on a widget (`self.mFrame`), if it's master is a `Canvas`. Try using [this](https://stackoverflow.com/a/66215091/11106801). It acts just like a regular `tkinter.Frame`. – TheLizzard Jul 29 '21 at 08:57
  • Is that documented anywhere? it fits with my experience but I would prefer to use grid if I could. – user2801282 Jul 29 '21 at 14:11

1 Answers1

0
import tkinter as tk
from tkinter import messagebox

def flexx(o, r = 0, c = 0, rw = 1, cw = 1):
    """flexx is used on other widgets (Text, Canvas, Listbox, Button etc)"""
    if r !=  None:
        o.grid_rowconfigure(r, weight = rw)
    if c !=  None:
        o.grid_columnconfigure(c, weight = cw)

class test:

    def callback( self ):
        pass

    def screencenter( self, o ):
        w, h = o.winfo_width(  ), o.winfo_height(  )
        x, y = o.winfo_screenwidth( ) - w, o.winfo_screenheight( ) - h
        o.geometry( f'{w}x{h}+{int(x/2)}+{int(y/2)}' )

    def closer(self, ev):
        self.master.destroy()

    def __init__(self, hxw):
        self.master = tk.Tk()
        self.master.withdraw()
        self.hxw = hxw
        self.master.wm_title("Test")
        self.master.configure(bd = 2)
        for r in range(4):
            flexx(self.master, r =  r)
        self.master.bind("<Escape>", self.closer)
        sw = self.master.winfo_screenwidth()
        sh = self.master.winfo_screenheight()
        if not self.hxw:
            self.hxw = f"{min(940,sw)}x{min(660,sh)}"
        # First section 7 lines
        self.topFrame = tk.Frame(
            self.master, height = 25, bg = "blue")
        self.topFrame.grid(row = 0,column = 0, sticky = "new")
        flexx(self.topFrame)
        self.helper = tk.Button(
            self.topFrame, text = "Help", command = self.callback)
        self.helper.grid(row = 0, column = 0, sticky = "e")
        # Second section 29
        self.hsFrame = tk.Frame(self.master, bg = "red")
        self.hsFrame.grid(row = 1, column = 0, sticky = "nsew")
        flexx(self.hsFrame, r = 1)
        self.hcanvas = tk.Canvas(self.hsFrame, bg = "green")
        self.hcanvas.grid(row = 0, column = 0, sticky = "nsew")
        self.mFrame = tk.Frame(self.hsFrame, bg = "cyan")
        self.mFrame.grid(row = 1, column = 0, sticky = "new")
        flexx(self.mFrame, r = 0)
        self.mFrame.bind(
            "<Configure>",
            lambda e: self.hcanvas.configure(
                scrollregion = self.hcanvas.bbox("all") ))
        item = self.hcanvas.create_window( (400,20), window = self.mFrame)
        self.labelFrame = tk.LabelFrame(self.mFrame, bg = "magenta")
        self.labelFrame.grid(row = 0,column = 0, sticky = "new")
        for col in range(20):
            tkl = tk.Label(
                self.labelFrame, width = 10, bd = 1,
                relief = "solid", text = f"col {col+1}")
            tkl.grid(row = 0, column = col, sticky = "nsew")
        hsbar = tk.Scrollbar(
            self.hsFrame, orient = "horizontal", command = self.hcanvas.xview)
        hsbar.grid(row = 2, column = 0, sticky = "ew")
        self.hcanvas.configure(xscrollcommand = hsbar.set)
        # Third section 38 lines
        self.HsFrame = tk.Frame(self.master, bg = "red")
        self.HsFrame.grid(row = 4, column = 0, sticky = "nsew")
        flexx(self.HsFrame, r = 4)
        self.Hcanvas = tk.Canvas(self.HsFrame, bg = "green")
        self.Hcanvas.grid(row = 0, column = 0, sticky = "nsew")
        self.MFrame = tk.Frame(self.HsFrame, bg = "cyan")
        self.MFrame.grid(row = 1, column = 0, sticky = "new")
        flexx(self.MFrame, r = 0)
        self.MFrame.bind(
            "<Configure>",
            lambda e: self.Hcanvas.configure(
                scrollregion = self.Hcanvas.bbox("all") ))
        item1 = self.Hcanvas.create_window( (400,20), window = self.MFrame)
        self.LabelFrame = tk.LabelFrame(self.MFrame, bg = "magenta")
        self.LabelFrame.grid(row = 0,column = 0, sticky = "new")
        rows = columns = 20
        for i in range(rows):
            for j in range(columns):
                tkl = tk.Label(
                    self.LabelFrame, width = 10, borderwidth = 1,
                    relief = "solid", text = f"{i+1},{j+1}")
                tkl.grid(row = i, column = j, sticky = "nsew")
                flexx(self.LabelFrame, r = i, c = j)
        Hsbar = tk.Scrollbar(
            self.HsFrame, orient = "horizontal", command = self.Hcanvas.xview)
        Hsbar.grid(row = 2, column = 0, sticky = "ew")
        self.Hcanvas.configure(xscrollcommand = Hsbar.set)
        Vsbar = tk.Scrollbar(
            self.HsFrame, orient = "vertical", command = self.Hcanvas.yview)
        Vsbar.grid(row = 0, column = 1, sticky = "ns")
        self.Hcanvas.configure(yscrollcommand = Vsbar.set)
        # set size and center it
        self.master.geometry(self.hxw) # Width x Height
        self.master.update()
        self.screencenter( self.master )
        self.master.deiconify()

if __name__ == "__main__":

    SpreadSheet = test(None)
    SpreadSheet.master.mainloop()
Derek
  • 1,916
  • 2
  • 5
  • 15
  • I added rowconfigure and columnconfigure and now the window is completely filled. the next problem is that the line of column headers appears in the middle of the window and there is still no data. – user2801282 Jul 29 '21 at 14:09
  • I've managed to get the blue, green and orange objects visible button still can not view buttons. I'll append my answer to include a function called `flexx`, it will reduce the bulk of your program and make it much easier to read. – Derek Jul 30 '21 at 00:37
  • So how is it going? Have you managed to view the buttons and scrollbars? I've had some success but it's difficult to explain. There are a number of problems with objects over laying each other. I have managed to view buttons and all three scrollbars. If you would like to see the code I have working just ask. – Derek Jul 30 '21 at 14:38
  • I changed the code to be more readable. I would be interested in your code, but there should only be two scroll bars. If you want to see what I want it to look like, open a spreadsheet and freeze the first row. Now the scroll bars should do what I am looking for. – user2801282 Jul 31 '21 at 05:32
  • Ok, I divided your code into sections and found the first part worked with little modification while the second part was almost the same but had more bugs so I duplicated the first section - that's where the extra scrollbar comes in. I'll post those modifications now. It's 116 lines – Derek Jul 31 '21 at 05:58