This script when run, opens a window that is divided into 3 frames:
The big frame will have a lot of data (= label-widgets) so I need it to be scrollable (vertically). This I have done by creating a canvas widget alonside a scrollbar widget. In the canvas, a frame widget is placed.
Everything seems to be working, however my resizing function does not. My frame widget does not get its dimensions updated! This is probably because of an error that I can't manage to fix.
Fundamental question:
The script gives an error on the "lambda: resize_frame(self)" command on line 43. How do i fix this?
Side-note: My issue probably has more to do with an improper binding on the canvas widget. Because I'm not sure I wanted to give enough context (script).
Many thanks in advance.
from Tkinter import *
import math
class Processing(Toplevel):
def __init__(self, master, *args, **kwargs):
Toplevel.__init__(self, master)
self.master = master
self.title("Process Window")
for r in range(6):
self.rowconfigure(r, weight = 1)
for c in range(4):
self.columnconfigure(c, weight = 1)
### WINDOW size and position definitions ###
ScreenSizeX = master.winfo_screenwidth()
ScreenSizeY = ( master.winfo_screenheight() - 75 ) #about 75pixels for taskbar on bottom of screen (Windows)
ScreenRatio = 0.9
FrameSizeX = int(ScreenSizeX * ScreenRatio)
FrameSizeY = int(ScreenSizeY * ScreenRatio)
FramePosX = (ScreenSizeX - FrameSizeX)/2
FramePosY = (ScreenSizeY - FrameSizeY)/2
self.geometry("%sx%s+%s+%s"%(FrameSizeX,FrameSizeY,FramePosX,FramePosY))
### Creating 3 "sub-frames" ###
# Frame 1 - canvas container with scrollbar#
self.Canvas1 = Canvas(self, bg = "white")
self.Canvas1.grid(row = 0, column = 0, rowspan = 5, columnspan = 4, sticky = N+E+S+W)
self.Canvas1.rowconfigure(1, weight = 1)
self.Canvas1.columnconfigure(1, weight = 1)
self.myscrollbar=Scrollbar(self, orient = "vertical", command = self.Canvas1.yview)
self.Canvas1.configure(yscrollcommand = self.myscrollbar.set)
self.myscrollbar.grid(row = 0, column = 4, rowspan = 5, sticky = N+S)
# Frame 1 - Frame widget in canvas #
self.Frame1 = Frame(self.Canvas1, bg = "white")
self.Frame1.rowconfigure(0, weight = 1)
for c in range(2):
self.Frame1.columnconfigure(1 + (2 * c), weight = 1)#1,3 - columns for small icons in the future
for cb in range(3):
self.Frame1.columnconfigure((cb * 2), weight = 9)#0,2,4 - columns for data
self.CFrame1 = self.Canvas1.create_window(0, 0, window = self.Frame1, width = FrameSizeX, anchor = N+W)
self.Canvas1.bind("<Configure>", lambda: resize_frame(self)) # !!!!! Doesn't work & gives error !!!!!! #
self.Frame1.bind("<Configure>", lambda: scrollevent(self))
self.Canvas1.config(scrollregion=self.Canvas1.bbox("all"))
# Frame 2 #
self.Frame2 = Frame(self, bg= "yellow")
self.Frame2.grid(row = 5, column = 0, rowspan = 1, columnspan = 3, sticky = W+E+N+S)
for r in range(3):
self.Frame2.rowconfigure(r, weight=1)
for c in range(3):
self.Frame2.columnconfigure(c, weight = 1)
# Frame 3 #
self.Frame3 = Frame(self)
self.Frame3.grid(row = 5, column = 3, rowspan = 1, columnspan = 2, sticky = W+E+N+S)
self.Frame3.rowconfigure(0, weight = 1)
self.Frame3.columnconfigure(0, weight = 1)
# Propagation #
#self.grid_propagate(False) # All widgets (the 3 subframes) need to fit in Toplevel window. Minimal window size will be implemented later.
self.Canvas1.grid_propagate(False) # canvas works with scrollbar, widgets dont need to fit in window size.
#self.Frame1.grid_propagate(False) # Frame1 should resize to hold all data (label-widgets)
self.Frame2.grid_propagate(False) # fixed frame dimensions
self.Frame3.grid_propagate(False) # fixed textbox dimensions
self.Frame1.update_idletasks() # just to make sure
### Widgets for the multiple frames ###
# Frame1 - further populated by button command in frame 2#
self.lblaa = Label(self.Frame1, bg="white", text = "Processing...", justify = "left")
self.lblaa.grid(row = 0, column = 0, sticky = N+W)
self.LSlabelsr = []
self.LSlabelsa = []
self.LSlabelsb = []
# Frame 2 #
self.Wbuttontest=Button(self.Frame2, text="Start listing test", command = lambda: refresh(self))
self.Wbuttontest.grid(row = 0, column = 0, columnspan = 3)
self.Wentry = Entry(self.Frame2)
self.Wentry.grid(row = 2, column = 0, columnspan = 3, sticky = E+W, padx = 10)
self.Wentry.delete(0, END)
self.Wentry.insert(0, "user input here")
# Frame3 #
self.Wtext = Text(self.Frame3)
self.Wscrollb = Scrollbar(self.Frame3)
self.Wscrollb.config(command = self.Wtext.yview)
self.Wtext.config(yscrollcommand = self.Wscrollb.set)
self.Wtext.grid(row = 0, column = 0, sticky = N+E+W+S)
### Test-Lists ### Last character in the left column is "ez" !! ###
self.LSa = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
self.LSb = [1, 2, 3, 4, 66, 6, 7, 8, 9, 67, 11, 12, 13, 14, 68]
self.LSr = []
ib = 0
prefix = ""
for i in range(104):
if ib > 25:
prefix = chr(ord("a") + (i/ib - 1))
ib = 0
else:
pass
self.LSr.append(prefix + chr(97+ib))
ib += 1
### FUNCTIONS ###
def resize_frame(self, event):
self.Canvas1.itemconfig(self.CFrame1, width = e.width) #height of frame should depend on the contents.
def scrollevent(event):
self.Canvas1.configure(scrollregion=self.Canvas1.bbox("all"),width=200,height=200)
def refresh(self): ### Button-command: data will be shown ###
if self.lblaa.winfo_exists() == 1:
self.lblaa.destroy()
for i in range(len(self.LSr)):
self.Frame1.rowconfigure(i, weight = 0)
del self.LSlabelsr[:] # remove any previous labels from if the callback was called before
del self.LSlabelsa[:] # remove any previous labels from if the callback was called before
del self.LSlabelsb[:] # remove any previous labels from if the callback was called before
Vlabelheight = 1
# Left List #
for i in range(len(self.LSr)):
self.LSlabelsr.append(Label(self.Frame1, text = str(self.LSr[i]), bg = "LightBlue", justify = "left", height = Vlabelheight))
self.LSlabelsr[i].grid(row = i, column = 0, sticky = E+W)
# Middle List #
for i in range(len(self.LSa)):
self.LSlabelsa.append(Label(self.Frame1, text = str(self.LSa[i]), bg = "LightBlue", fg = "DarkViolet", justify = "left", height = Vlabelheight))
self.LSlabelsa[i].grid(row = i, column = 2, sticky = E+W)
# Right List #
for i in range(len(self.LSb)):
self.LSlabelsb.append(Label(self.Frame1, text = str(self.LSb[i]), bg = "LightBlue", fg = "DarkGreen", justify = "left", height = Vlabelheight))
self.LSlabelsb[i].grid(row = i, column = 4, sticky = E+W)
self.Frame1.update()
self.Frame1.update_idletasks()
print("done")
if __name__ == "__main__":
root = Tk()
root.title("Invisible")
root.resizable(FALSE,FALSE)
root.withdraw()
app = Processing(root)
root.mainloop()
Working version after suggestions by R4PH4EL:
Some tweaks on the lambda commands on line 43/107 & 44/110.
from Tkinter import *
import math
class Processing(Toplevel):
def __init__(self, master, *args, **kwargs):
Toplevel.__init__(self, master)
self.master = master
self.title("Process Window")
for r in range(6):
self.rowconfigure(r, weight = 1)
for c in range(4):
self.columnconfigure(c, weight = 1)
### WINDOW size and position definitions ###
ScreenSizeX = master.winfo_screenwidth()
ScreenSizeY = ( master.winfo_screenheight() - 75 ) #about 75pixels for taskbar on bottom of screen (Windows)
ScreenRatio = 0.9
FrameSizeX = int(ScreenSizeX * ScreenRatio)
FrameSizeY = int(ScreenSizeY * ScreenRatio)
FramePosX = (ScreenSizeX - FrameSizeX)/2
FramePosY = (ScreenSizeY - FrameSizeY)/2
self.geometry("%sx%s+%s+%s"%(FrameSizeX,FrameSizeY,FramePosX,FramePosY))
### Creating 3 "sub-frames" ###
# Frame 1 - canvas container with scrollbar#
self.Canvas1 = Canvas(self, bg = "white")
self.Canvas1.grid(row = 0, column = 0, rowspan = 5, columnspan = 4, sticky = N+E+S+W)
self.Canvas1.rowconfigure(1, weight = 1)
self.Canvas1.columnconfigure(1, weight = 1)
self.myscrollbar=Scrollbar(self, orient = "vertical", command = self.Canvas1.yview)
self.Canvas1.configure(yscrollcommand = self.myscrollbar.set)
self.myscrollbar.grid(row = 0, column = 4, rowspan = 5, sticky = N+S)
# Frame 1 - Frame widget in canvas #
self.Frame1 = Frame(self.Canvas1, bg = "white")
self.Frame1.rowconfigure(0, weight = 1)
for c in range(2):
self.Frame1.columnconfigure(1 + (2 * c), weight = 1)#1,3 - columns for small icons in the future
for cb in range(3):
self.Frame1.columnconfigure((cb * 2), weight = 9)#0,2,4 - columns for data
self.CFrame1 = self.Canvas1.create_window(0, 0, window = self.Frame1, width = FrameSizeX, anchor = N+W)
self.Canvas1.bind("<Configure>", lambda event: self.resize_frame(event)) # !!!!! Doesn't work & gives error !!!!!! #
self.Frame1.bind("<Configure>", lambda event: self.scrollevent(event))
self.Canvas1.config(scrollregion=self.Canvas1.bbox("all"))
# Frame 2 #
self.Frame2 = Frame(self, bg= "yellow")
self.Frame2.grid(row = 5, column = 0, rowspan = 1, columnspan = 3, sticky = W+E+N+S)
for r in range(3):
self.Frame2.rowconfigure(r, weight=1)
for c in range(3):
self.Frame2.columnconfigure(c, weight = 1)
# Frame 3 #
self.Frame3 = Frame(self)
self.Frame3.grid(row = 5, column = 3, rowspan = 1, columnspan = 2, sticky = W+E+N+S)
self.Frame3.rowconfigure(0, weight = 1)
self.Frame3.columnconfigure(0, weight = 1)
# Propagation #
#self.grid_propagate(False) # All widgets (the 3 subframes) need to fit in Toplevel window. Minimal window size will be implemented later.
self.Canvas1.grid_propagate(False) # canvas works with scrollbar, widgets dont need to fit in window size.
#self.Frame1.grid_propagate(False) # Frame1 should resize to hold all data (label-widgets)
self.Frame2.grid_propagate(False) # fixed frame dimensions
self.Frame3.grid_propagate(False) # fixed textbox dimensions
self.Frame1.update_idletasks() # just to make sure
### Widgets for the multiple frames ###
# Frame1 - further populated by button command in frame 2#
self.lblaa = Label(self.Frame1, bg="white", text = "Processing...", justify = "left")
self.lblaa.grid(row = 0, column = 0, sticky = N+W)
self.LSlabelsr = []
self.LSlabelsa = []
self.LSlabelsb = []
# Frame 2 #
self.Wbuttontest=Button(self.Frame2, text="Start listing test", command = lambda: self.refresh())
self.Wbuttontest.grid(row = 0, column = 0, columnspan = 3)
self.Wentry = Entry(self.Frame2)
self.Wentry.grid(row = 2, column = 0, columnspan = 3, sticky = E+W, padx = 10)
self.Wentry.delete(0, END)
self.Wentry.insert(0, "user input here")
# Frame3 #
self.Wtext = Text(self.Frame3)
self.Wscrollb = Scrollbar(self.Frame3)
self.Wscrollb.config(command = self.Wtext.yview)
self.Wtext.config(yscrollcommand = self.Wscrollb.set)
self.Wtext.grid(row = 0, column = 0, sticky = N+E+W+S)
### Test-Lists ### Last character in the left column is "ez" !! ###
self.LSa = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
self.LSb = [1, 2, 3, 4, 66, 6, 7, 8, 9, 67, 11, 12, 13, 14, 68]
self.LSr = []
ib = 0
prefix = ""
for i in range(104):
if ib > 25:
prefix = chr(ord("a") + (i/ib - 1))
ib = 0
else:
pass
self.LSr.append(prefix + chr(97+ib))
ib += 1
### FUNCTIONS ###
def resize_frame(self, e):
self.Canvas1.itemconfig(self.CFrame1, width = e.width) #height of frame should depend on the contents.
def scrollevent(self, event):
self.Canvas1.configure(scrollregion=self.Canvas1.bbox("all"),width=200,height=200)
def refresh(self): ### Button-command: data will be shown ###
if self.lblaa.winfo_exists() == 1:
self.lblaa.destroy()
for i in range(len(self.LSr)):
self.Frame1.rowconfigure(i, weight = 0)
del self.LSlabelsr[:] # remove any previous labels from if the callback was called before
del self.LSlabelsa[:] # remove any previous labels from if the callback was called before
del self.LSlabelsb[:] # remove any previous labels from if the callback was called before
Vlabelheight = 1
# Left List #
for i in range(len(self.LSr)):
self.LSlabelsr.append(Label(self.Frame1, text = str(self.LSr[i]), bg = "LightBlue", justify = "left", height = Vlabelheight))
self.LSlabelsr[i].grid(row = i, column = 0, sticky = E+W)
# Middle List #
for i in range(len(self.LSa)):
self.LSlabelsa.append(Label(self.Frame1, text = str(self.LSa[i]), bg = "LightBlue", fg = "DarkViolet", justify = "left", height = Vlabelheight))
self.LSlabelsa[i].grid(row = i, column = 2, sticky = E+W)
# Right List #
for i in range(len(self.LSb)):
self.LSlabelsb.append(Label(self.Frame1, text = str(self.LSb[i]), bg = "LightBlue", fg = "DarkGreen", justify = "left", height = Vlabelheight))
self.LSlabelsb[i].grid(row = i, column = 4, sticky = E+W)
self.Frame1.update()
self.Frame1.update_idletasks()
print("done")
if __name__ == "__main__":
root = Tk()
root.title("Invisible")
root.resizable(FALSE,FALSE)
root.withdraw()
app = Processing(root)
root.mainloop()
Either your indentation is wrong or your getting something wrong in general.
All of your functions are defined inside your __init__
function
Second: if you want to call a class function, you call it by obj.function
Your error on lambda: resize(self)
may occur as it should be lambda: self.resize
.
Give it a shot with this one and try it. And please make sure your indentations are correct.
I totally agree with Bryan here - ommiting the lambdas it would be (my personal opinion) easier to read and kind of "better" in a meaning of more structured and pragmatic coding style.