How to make listbox fill whole frame using grid manager? - python

Consider the following tk interface;
There are two frames, each in its own column managed by grid geometry manager. I tried several sticky options, but I couldn't make the listbox look longer. I want it to span acroos the whole row. How can I achieve it? Here are my codes;
import tkinter as tk
class MinimalTestCase(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master)
self.f1 = tk.Frame(self)
self.f2 = tk.Frame(self)
self.f1.grid(row=0, column=0)
self.f2.grid(row=0, column=1, sticky=(tk.N, tk.S, tk.E, tk.W))
### Fill left frame with dummy elements to demonstrate the problem
for i in range(15):
tk.Label(self.f1, text="Label{}".format(i)).grid(row=i)
### Put listbox on right frame
self.lbox = tk.Listbox(self.f2)
self.lbox.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.E, tk.W))
self.grid()
if __name__ == "__main__":
root=tk.Tk()
MinimalTestCase(root)
root.mainloop()

You have to use rowconfigure(0, weight=1) - not with Listbox but with its parent which manage this geometry (Frame).
import Tkinter as tk
class MinimalTestCase(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master)
self.f1 = tk.Frame(self)
self.f2 = tk.Frame(self)
self.f2.rowconfigure(0, weight=1) # <-- row 0 will be resized
self.f1.grid(row=0, column=0)
self.f2.grid(row=0, column=1, sticky=(tk.N, tk.S, tk.E, tk.W))
### Fill left frame with dummy elements to demonstrate the problem
for i in range(15):
tk.Label(self.f1, text="Label{}".format(i)).grid(row=i)
### Put listbox on right frame
self.lbox = tk.Listbox(self.f2)
self.lbox.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.E, tk.W))
self.grid()
if __name__ == "__main__":
root=tk.Tk()
MinimalTestCase(root)
root.mainloop()
Tkinterbook: The Tkinter Grid Geometry Manager

Related

How to prevent multiple windows from popping up in tkinter?

import tkinter as tk
from tkinter import *
from tkinter import ttk
LARGE_FONT = ("Verdana", 12)
class pages(tk.Tk):
#starts us off in the login page
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "ScanNET")
tk.Tk.wm_minsize(self, 800, 800)
container = tk.Frame(self)
container.pack(side=TOP, fill=BOTH, expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (loginpage, GUI):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky=N+E+S+W)
self.show_frame(loginpage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class loginpage(tk.Frame):
#login page content
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
loginlabel = tk.Label(self, text="login page", font=LARGE_FONT)
loginlabel.pack(padx=10, pady=10)
#button moves you to gui
loginbutton1 = tk.Button(self, text= "Go to GUI", command=lambda: controller.show_frame(GUI))
loginbutton1.pack()
class GUI(tk.Frame):
def __init__(self, parent, controller):
#all widths and heights aren't official, most likely change
tk.Frame.__init__(self, parent)
self.root = tk.Tk()
#the tabs
my_notebook = ttk.Notebook(self.root)
my_notebook.pack()
devicestab = Frame(my_notebook, width=800, height=600)
reportstab = Frame(my_notebook, width=800, height=600)
devicestab.pack(fill=BOTH, expand=1)
reportstab.pack(fill=BOTH, expand=1)
my_notebook.add(devicestab, text="Devices")
my_notebook.add(reportstab, text="Reports")
#contents for devices tab
devicesleft = LabelFrame(devicestab, text="Devices found: ", padx=5, pady=5, width=500, height=600)
devicesleft.grid(row=0, column=0)
devicesright = LabelFrame(devicestab, text="Activity Feed: ", padx=5, pady=5, width=300 , height=600)
devicesright.grid(row=0, column=1)
#contents for reports tab
reportsleft = LabelFrame(reportstab, text="Report Summaries: ", padx=5, pady=5, width=400 , height=600)
reportsleft.grid(row=0, column=0)
reportsright= LabelFrame(reportstab, text="Charts and Diagrams: ", padx=5, pady=5, width=400 , height=600)
reportsright.grid(row=0, column=1)
app = pages()
app.mainloop()
When I run this, both the loginpage and GUI windows open. Correct me if I'm wrong, but I think the problem is probably around the
tk.Frame.__init__(self, parent)
self.root = tk.Tk()
my_notebook = ttk.Notebook(self.root)
part in the GUI class. I've searched everywhere and I can't seem to find a way to have a first page as a login page which will move to a second page that has tabs using notebook. I feel as if something else has to be in the ttk.Notebook() part, and perhaps remove self.root = tk.Tk() after. I'd love to hear what y'all think.
I am assuming you want the notebook in the same widget of the rest, so you should not use tk.Tk() and then you place the notebook in the parent which is already your root. Check the code in the end of my answer. Also, since there was a lot of problems with your code I made some changes and comments that will help you to write better codes in tkinter. Please read it carefully. You may also want to study the effbot web page.
import tkinter as tk
# from tkinter import * # just don't do this
from tkinter import ttk
LARGE_FONT = ("Verdana", 12)
# class pages(tk.Tk):
class Pages(tk.Tk): # class names should start with upper case
#starts us off in the login page
# def __init__(self, *args, **kwargs):
def __init__(self):
# tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.__init__(self)
# tk.Tk.wm_title(self, "ScanNET")
self.winfo_toplevel().title("ScanNET")
# tk.Tk.wm_minsize(self, 800, 800)
self.wm_minsize(800, 800) # since you defined tk.Tk as pages parent you can call Tk methods directly
container = tk.Frame(self)
# container.pack(side=TOP, fill=BOTH, expand=True)
# container.grid_rowconfigure(0, weight=1)
# container.grid_columnconfigure(0, weight=1)
container.grid(row=0, column = 0) # don't use pack if you want to use grid
self.frames = {}
for F in (loginpage, GUI):
frame = F(container, self)
self.frames[F] = frame
# frame.grid(row=0, column=0, sticky=N+E+S+W)
frame.grid(row=0, column=0, sticky='NESW') #since we are not importing all we are not importing tk.W but you can use string instead
self.show_frame(loginpage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class loginpage(tk.Frame):
#login page content
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
loginlabel = tk.Label(self, text="login page", font=LARGE_FONT)
loginlabel.pack(padx=10, pady=10)
#button moves you to gui
loginbutton1 = tk.Button(self, text= "Go to GUI", command=lambda: controller.show_frame(GUI))
loginbutton1.pack()
class GUI(tk.Frame):
def __init__(self, parent, controller):
#all widths and heights aren't official, most likely change
tk.Frame.__init__(self, parent)
# self.root = tk.Tk() # don't create new Tk objects, you just need one. The others should be Toplevel objects
### self.root = tk.Toplevel() ### this would be the correct way of creating a new window but you don't want to do that here your root is your parent
#the tabs
# my_notebook = ttk.Notebook(self.root)
my_notebook = ttk.Notebook(self) # this is how you place the notebook in the Frame widget and not in a new one
# my_notebook.pack()
my_notebook.grid() # we are now using grid so it will not accept pack anymore
# devicestab = Frame(my_notebook, width=800, height=600)
devicestab = tk.Frame(my_notebook, width=800, height=600) # again, since we are not importing al we have to use tk. before tkinter methods
# reportstab = Frame(my_notebook, width=800, height=600)
reportstab = tk.Frame(my_notebook, width=800, height=600)
# devicestab.pack(fill=BOTH, expand=1)
devicestab.pack(fill="both", expand=1) # instead of tk.BOTH we can use "both"
reportstab.pack(fill="both", expand=1)
my_notebook.add(devicestab, text="Devices")
my_notebook.add(reportstab, text="Reports")
#contents for devices tab
devicesleft = tk.LabelFrame(devicestab, text="Devices found: ", padx=5, pady=5, width=500, height=600)
devicesleft.grid(row=0, column=0)
devicesright = tk.LabelFrame(devicestab, text="Activity Feed: ", padx=5, pady=5, width=300 , height=600)
devicesright.grid(row=0, column=1)
#contents for reports tab
reportsleft = tk.LabelFrame(reportstab, text="Report Summaries: ", padx=5, pady=5, width=400 , height=600)
reportsleft.grid(row=0, column=0)
reportsright= tk.LabelFrame(reportstab, text="Charts and Diagrams: ", padx=5, pady=5, width=400 , height=600)
reportsright.grid(row=0, column=1)
app = Pages()
app.mainloop()

tkinter - how to add scrollbar to all tabs (canvas)

How can i add scrollbar to my Frame in tkinter? I would like to add scrollbar in Comments_tab.
I was trying to play with Canvas, however, i am doing something wrong. Could i ask for your advice please?
class Main(tk.Tk):
def __init__(self):
#
tk.Tk.__init__(self)
#make notebook fill display
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
#Notebook
nb = ttk.Notebook(self)
nb.grid(row=0, column=0, sticky='nswe')
#keep a reference to the pages
self.p0 = Config_tab(self)
self.p1 = Comments_tab(self)
#Canvas
canvas_comments=tk.Canvas(self.p1,bg='#FFFFFF',width=300,height=300,scrollregion=(0,0,500,500))
vbar=tk.Scrollbar(self.p1,orient=tk.VERTICAL)
vbar.grid(row=0, column=0, sticky="NSE")
vbar.config(command=canvas_comments.yview)
tk.Label(self.p1, text="asdddddd").grid(row=0,column=0)
canvas_comments.config(width=300,height=300)
canvas_comments.config(yscrollcommand=vbar.set)
canvas_comments.grid(row=0, column=0, sticky="NSEW")
#tabs
nb.add(self.p0, text="Config")
nb.add(self.p1, text="Comments")
class Comments_tab(tk.Frame):
def __init__(self, master, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
tk.Label(self, text="HU3: ").grid(row=3, column=0)
if __name__ == "__main__":
root = Main()
root.geometry('800x600')
root.title("HAT")
root.mainloop()

Resizing frames in main window to fit the window

I have the menu frame on the left side and the main container on the right side but when I fire it up I only see them in a small box inside the main window.
How can I make the frames fit the window no matter what size it is?
Code:
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("GUI Project")
# self.resizable(0, 0)
menu = tk.Frame(self, relief="solid")
container = tk.Frame(self, relief="ridge")
menu.grid(row=0, column=0, rowspan=4, sticky="nsew")
container.grid(row=0, column=1, sticky="nsew")
menu.grid_rowconfigure(0, weight=1)
container.rowconfigure(0, weight=0)
container.columnconfigure(1, weight=0)
self.frames = ["Menu", "FutureFeature2", "TestPing", "FutureFeature3", "FutureFeature"]
self.frames[0] = Menu(parent=menu, controller=self)
self.frames[1] = FutureFeature2(parent=container, controller=self)
self.frames[2] = TestPing(parent=container, controller=self)
self.frames[3] = FutureFeature3(parent=container, controller=self)
self.frames[4] = FutureFeature(parent=container, controller=self)
self.frames[0].grid(row=0, column=0, sticky="nsew")
self.frames[1].grid(row=0, column=0, sticky="nsew")
self.frames[2].grid(row=0, column=0, sticky="nsew")
self.frames[3].grid(row=0, column=0, sticky="nsew")
self.frames[4].grid(row=0, column=0, sticky="nsew")
self.show_frame(1)
def show_frame(self, page_name):
frame = self.frames[page_name]
print(frame)
frame.tkraise()
frame.grid(row=0, column=0, columnspan=5, rowspan=5, sticky="nsew")
class Menu(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
button1 = tk.Button(self, text="Ping Test", bg="royalblue2",
command=lambda: controller.show_frame(2))
button2 = tk.Button(self, text="FutureFeature", bg="dark violet",
command=lambda: controller.show_frame(4))
buttun3 = tk.Button(self, text="FutureFeature", bg="pale goldenrod",
command=lambda: controller.show_frame(1))
button4 = tk.Button(self, text="Quit", bg="gray40",
command=lambda: self.terminate())
button1.pack(fill="both", expand=True)
button2.pack(fill="both", expand=True)
buttun3.pack(fill="both", expand=True)
button4.pack(fill="both", expand=True)
def terminate(self):
path = fr'c:/users/{os.getlogin()}/desktop/Gui-Skeleton'
try:
os.rmdir(path)
except OSError as err:
print(f"Error Deleting tmp folder! {err}")
exit()
if __name__ == "__main__":
path = fr'c:/users/{os.getlogin()}/desktop/Gui-Skeleton'
try:
if os.path.isdir(path):
pass
else:
os.mkdir(path)
except OSError as err:
print(f"[!] Operation failed! {err}")
app = GUI()
app.geometry("800x600")
app.mainloop()
.....................................................................................................................................................................................................................................................................................
The specific problem that you're asking about is due to the fact you are putting menu and container in the root window using grid but you haven't given any rows or columns in the root window a weight. Therefore, both menu and grid will use as little space as necessary.
A general rule of thumb is that when you use grid, you should give at least one row and one column a positive weight so that there is no unused space. You are failing to do that in the root window. For more information on grid weights see What does 'weight' do in tkinter?
The solution is simple: make sure that you've given at least one row and one column a positive weight in the root window:
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1)
That being said, I would recommend using pack for menu and container since they are the only widgets directly in the root window. pack is generally better than grid when laying out widgets in row or column, if for no other reason that you don't have to take the extra step of assigning weights.
menu.pack(side="left", fill="y")
container.pack(side="right", fill="both", expand=True)

How to add a scrollbar that orientes its position on the top most widget

I have a tkinter app in which I have a main canvas with multiple pages (all of which are frames). I pull up the different pages by rasing them with the frame.tkraise() command. I now want to add a scrollbar to the whole thing. The scrollbar appears but without a slider and I am not sure if it cna recognize the change of page.
import tkinter as tk
from tkinter import ttk
class Economics(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
tk.Tk.columnconfigure(self, 0, weight=1)
tk.Tk.rowconfigure(self, 0, weight=1)
self.container = tk.Canvas()
self.container.grid(row=0, column=0, sticky="nsew")
self.container.columnconfigure("all", weight=1)
self.container.rowconfigure("all", weight=1)
self.vscrollbar = tk.Scrollbar(orient="vertical", command=self.container.yview)
self.container.configure(yscrollcomman=self.vscrollbar.set)
self.vscrollbar.grid(row=0, column=1, sticky="ns")
self.frames = {}
for F in (StartPage, ExamplePage1, ExamplePage2): # TUPLE OF PAGES
frame = F(self.container, self)
self.frames[F] = frame
self.show_frame(StartPage)
def show_frame(self, cont):
self.container.delete("all")
frame = self.frames[cont]
self.container.create_window(0, 0, anchor="nw", window=frame)
class StartPage(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
button_1 = ttk.Button(self, text="Example Page 1",
command=lambda: controller.show_frame(ExamplePage1))
button_1.grid(row=0, column=0)
button_2 = ttk.Button(self, text="Example Page 2",
command=lambda: controller.show_frame(ExamplePage2))
button_2.grid(row=1, column=0)
class ExamplePage1(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
for i in range(50):
label = tk.Label(self, text="Button {} of 50".format(i+1))
label.grid(row=i, column=0)
button_back = ttk.Button(self, text="Back",
command=lambda: controller.show_frame(StartPage))
button_back.grid(row=0, column=1)
class ExamplePage2(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
for i in range(35):
label = tk.Label(self, text="Button {} of 35".format(i+1))
label.grid(row=i, column=0)
button_back = ttk.Button(self, text="Back",
command=lambda: controller.show_frame(StartPage))
button_back.grid(row=0, column=1)
app = Economics()
app.geometry("800x600")
app.resizable(True, True)
app.mainloop()
In this example file you can see the basic structure of my app with some example widgets and buttons. The scrollbar shows but without the slider. What do I have to change to get a working scrollbar for all pages.
Later on I'm planning to get a horizontal scrollbar as well.
You can't scroll items added to a canvas with pack, place, or grid. A canvas can only scroll items added via the canvas create_* functions, such as create_window.

frames layout with canvas using tkinter

I'm added canvas and a scroll bar to one of the frames in my script.
However somethings wrong cause the scroll bar is off (lower bottom is not visible) and the text I drew is off. Could anyone please tell me whats the problem ? I want the canvas to fill the whole frame (obviously without the scroll bar)
import sys
import os
if sys.version_info[0] < 3:
import Tkinter as tk
import ttk as ttk
else:
import tkinter as tk
import tkinter.ttk as ttk
#
# LeftMiddle
#
class LeftMiddle(tk.Frame):
def __init__(self, master=None):
self.parent = master
tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken")
self.__create_layout()
self.draw_text()
def __create_layout(self):
self.canvas = tk.Canvas(self, bg="green", relief=tk.SUNKEN)
self.canvas.config(width=20, height=10)
self.canvas.config(highlightthickness=0)
self.sbar = tk.Scrollbar(self, orient=tk.VERTICAL)
self.sbar.pack(side=tk.RIGHT, fill=tk.Y)
self.canvas.pack(side=tk.LEFT, expand="YES", fill=tk.BOTH)
def draw_text(self):
self.canvas.create_text(0, 0, text='1234567890', fill='red')
self.canvas.create_text(0, 25, text='ABCDEFGH', fill='blue')
#
# MainWindow
#
class MainWindow(tk.Frame):
def __init__(self, master=None):
self.parent = master
tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken")
self.__create_layout()
def __create_layout(self):
self.frame1 = tk.Frame(self, bg="yellow")
self.frame2 = tk.Frame(self, bg="blue")
self.frame3 = LeftMiddle(self) # tk.Frame(self, bg="green")
self.frame4 = tk.Frame(self, bg="brown")
self.frame5 = tk.Frame(self, bg="pink")
self.frame1.grid(row=0, column=0, rowspan=4, columnspan=8, sticky=(tk.N, tk.S, tk.W, tk.E))
self.frame2.grid(row=0, column=8, rowspan=4, columnspan=2, sticky=(tk.N, tk.S, tk.W, tk.E))
self.frame3.grid(row=4, column=0, rowspan=2, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E))
self.frame4.grid(row=4, column=5, rowspan=2, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E))
self.frame5.grid(row=5, column=0, rowspan=1, columnspan=10, sticky=(tk.N, tk.S, tk.W, tk.E))
for r in range(6):
self.rowconfigure(r, weight=1)
for c in range(10):
self.columnconfigure(c, weight=1)
#
# MAIN
#
def main():
root = tk.Tk()
root.title("Frames")
root.geometry("550x300+525+300")
root.configure(background="#808080")
root.option_add("*font", ("Courier New", 9, "normal"))
window = MainWindow(master=root)
window.pack(side="top", fill="both", expand=True)
root.mainloop()
if __name__ == '__main__':
main()
You have overlapping frames. Both self.frame3 and self.frame4 are in row 4 with a rowspan of 2, meaning they occupy rows 4 and 5. self.frame5 is also in row 5. So, self.frame5 is obscuring the bottom half of self.frame3, the frame that contains the canvas.
I don't understand why you have so many rowspans, they seem completely unnecessary unless you have some specific reason why you want multiple rows and columns but only single frames that span these rows and columns. Looking at the screenshot I see the need for only three rows.
The reason the text seems off is that by default the text is centered over the coordinate you give. You might want to look at the anchor option for the create_text method.

Categories

Resources