How to add a slide bar in a table? - python

I've been creating an app where there are Clients that I can add to a table, the problem is, I need a scrollbar to scroll through all the clients since the app Height is limited and the clients aren't.
Using tkinter I found a way to create a "table" using Entry and grid, but what if I want to create 100 rows? they would be outside of the view, so that's why the need of a scrollbar.
For those of you who know Java, I wanted to create something similar to Jtable, it has a method to create row, delete row, and it generates automatically that scrollbar as soon as the JTable runs out of space.
I've tried to use TkTable from ttk and mess around with some properties, but I preferred how Entries look.
root = Tk()
root.geometry("1200x900")
for i in range(10):
e = Entry(relief=RIDGE)
e.grid(row=i, column=2, sticky=N)
root.mainloop()
I created a root = Tk() and used root to grid them.
You'll see 10 Entries on top of the other.

When a window contains many widgets, they might not all be visible. However, neither a window (Tk or Toplevel instance) nor a Entry are scrollable.
One solution to make the window content scrollable is to put all the widgets in a Frame, and then, embed this Frame in a Canvas using the create_window method.
from tkinter import *
root = Tk()
canvas = Canvas(root)
scroll_y = Scrollbar(root, orient="vertical", command=canvas.yview)
frame = Frame(canvas)
# group of widgets
for i in range(100):
e = Entry(frame, relief=RIDGE, width = 100)
e.grid(row=i, column=2, sticky=N)
# put the frame in the canvas
canvas.create_window(0, 0, anchor='nw', window=frame)
# make sure everything is displayed before configuring the scrollregion
canvas.update_idletasks()
canvas.configure(scrollregion=canvas.bbox('all'),
yscrollcommand=scroll_y.set)
canvas.pack(fill='both', expand=True, side='left')
scroll_y.pack(fill='y', side='right')
root.mainloop()
output:

Related

Buttons not being placed inside the correct frame in tkinter

I am a newbie trying to use tkinter to build a GUI for an application. So far, I have a frame that I'd like to put several buttons into. However, every time I attempt to position this button, it isn't placed properly, being put outside of the frame itself. I wouldn't like to use the place function because of the several buttons I have to dynamically generate coming from an excel sheet so I was hoping to use the grid function instead.
Here is what I have so far
from tkinter import *
from customtkinter import *
window = Tk()
window.geometry("1920x1080")
window.state("zoomed")
window.title("My Company's Description Printer")
main_frame = CTkFrame(window, width=1920, height=1080, fg_color="grey21")
main_frame.place(x=0, y=0)
title = Label(main_frame,
text="My Company",
bg="grey21",
fg="white",
font=("Trajan Pro", 20)).place(x=626, y=30)
button_frame = CTkCanvas(main_frame,
width=800,
height=600,
highlightthickness=3,
highlightbackground="black",
relief="ridge",
bg="grey19").place(x=60, y=110)
test_button = CTkButton(button_frame, text="test").grid(row=0, column=0)
window.mainloop()
Example of code being ran
As you can see, the button is being placed in the top left corner of the entire window rather than the top left corner of the black bordered button frame. Any help would be appreciated. Thank you so much.
Note that button_frame is None because it is the result of .place(...), so the button (test_button is None as well due to same reason) is a child of the root window instead of the instance of CTkCanvas. .place(...) should be called in separate line.
Also .create_window() is used instead of tkinter layout manager to put widget into a canvas:
...
button_frame = CTkCanvas(main_frame,
width=800,
height=600,
highlightthickness=3,
highlightbackground="black",
relief="ridge",
bg="grey19")
# call .place(...) in separate line
button_frame.place(x=60, y=110)
test_button = CTkButton(button_frame, text="test") # don't use .grid(row=0, column=0)
# use .create_window() to put widget into canvas
button_frame.create_window(0, 0, window=test_button, anchor="nw")

Tkinter: cannot use geometry manager grid inside .!frame2 which already has slaves managed by pack

This is my first time using tkinter and I already did some research on pack and grid. How do I fix this code so that the pack and grid components don't intertwine?
I want to use grid for my checkbox so that 16 checkboxes show up in a column next to the words corresponding to them. Can I do this with pack?
# tkinter will help us with the GUI
import tkinter as tk
from tkinter import filedialog, Text
import os
def data():
categoriesArray = ["16 words here"]
for i in range(16):
checkbox = tk.Checkbutton(buttonFrame, bg="white")
checkbox.grid(row=i, column=0, sticky="w")
tk.Label(canvasFrame, text=categoriesArray[i]).grid(row=i, column=1, sticky="ew")
# Define the scrolling function for the scrollbar
def scrollFunction(event):
canvas.configure(scrollregion=canvas.bbox("all"), width=200, height=500)
# The root holds the whole app structure. Always attach to root.
root = tk.Tk()
# These two lines literally make the rectangular structure of the app.
canvas = tk.Canvas(root, height = 500, width= 1300, bg="#00008B")
canvas.pack()
# These two lines make the white screen you see on the left of the buttons.
frame = tk.Frame(root, bg="white")
frame.place(relwidth=0.8, relheight=0.8, relx=0.03, rely=0.1)
# This is the frame for the buttons on the right
buttonFrame = tk.Frame(root, bg="white")
buttonFrame.place(relwidth=0.13, relheight=0.8, relx=0.85, rely=0.1)
# You need a canvas to define a scrollbar within the app.
# Resource: https://stackoverflow.com/questions/16188420/tkinter-scrollbar-for-frame
canvas=tk.Canvas(buttonFrame)
canvasFrame=tk.Frame(canvas)
scrollbar=tk.Scrollbar(buttonFrame, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side="right", fill="y")
canvas.pack(side="left")
canvas.create_window((36,0), window=canvasFrame, anchor='nw')
canvasFrame.bind("<Configure>", scrollFunction)
# Call the data for the categories to show on the right
data()
# This runs the mainframe to work
root.mainloop()
Please let me know anything I can do to make my question better.
Places I've looked but gotten confused: fix this code 'cannot use geometry manager grid inside . which already has slaves managed by pack'
I fixed it. checkbox = tk.Checkbutton(buttonFrame, bg="white") should have canvasFrame instead of buttonFrame.

TKinter scrollbar for middle of screen, supporting resizeable screen with top bar and bottom bar

I am trying to make an application that displays a grid in the middle of the screen surrounded by two bars, a top bar and a bottom bar, which contain buttons for the user to press. These buttons should be able to display no matter where the user scrolls to on the grid and should not be cut off if the window is resized. I am struggling to configure the scrollbar to track the right area and to have the grid fall off the screen when the window is resized. Here is my code so far:
from tkinter import *
def add_row(event):
input_row = Entry(grid_frame, bd=1, text="", bg="white", relief="solid")
input_row.grid(row=grid_frame.rows, sticky=N+S+E+W)
Grid.rowconfigure(grid_frame, grid_frame.rows, weight=1)
grid_frame.rows = grid_frame.rows + 1
class GridFrame(Frame):
rows = 0
def __init__(self, root):
Frame.__init__(self, root, bd=1)
root = Tk(className="Main screen")
root.minsize(408, 80)
# size to quarter of screen
w, h = root.winfo_screenwidth() / 2, root.winfo_screenheight() / 2
root.geometry("%dx%d+0+0" % (w, h))
# grid_frame will resize and bars will not
Grid.rowconfigure(root, 1, weight=1)
Grid.columnconfigure(root, 0, weight=1)
myframe = Frame(root, bd=4, relief="groove")
myframe.grid(row=1, sticky=N + W + S + E)
canvas = Canvas(myframe)
grid_frame = GridFrame(canvas)
grid_frame.pack(fill=BOTH, expand=True)
grid_frame.bind("<Button-1>", add_row)
scrollbar = Scrollbar(myframe, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side=RIGHT, fill=Y)
canvas.pack(side=LEFT, fill=BOTH, expand=True)
topBar = Frame(root, grid_frame)
label = Label(topBar, text="Top Text")
label.pack()
topBar.grid(row=0, sticky=W+N+E+S)
bottomFrame = Frame(root, grid_frame)
label = Label(bottomFrame, text="Bottom Text")
label.pack()
bottomFrame.grid(row=2, sticky=E+S+W)
mainloop()
The scrollregion I want to track is the myframe/canvas/grid_frame combination I read to use from this post. The current functionality is that the scrollbar is never in an "active" state and rows added to the grid merely shrink the grid for it to fit within the display. To add a new row, click within the grid_frame region. Any help would be greatly appreciated! Here are some images of the current UI:
UI display with only a few rows
UI display with many more rows
There are two major problems with your code.
First, for the canvas to be able to scroll the inner frame, the inner frame must be a canvas object created with create_window. You're adding it to the canvas with pack, which means the canvas cannot scroll it.
To fix that, use create_window instead of pack:
canvas.create_window(0, 0, anchor="nw", window=grid_frame)
Second, you must reset the scrollregion attribute whenever the contents inside the canvas change. Normally this is done in a <Configure> event handler on the frame, but you can just as easily call it in your add_row function.
For example, add the following line to the end of add_row:
canvas.configure(scrollregion=canvas.bbox("all"))
With those two changes, the scrollbars will start to work as soon as the inner frame is taller than the canvas.
The above solves the problem of the inner window being able to scroll when you add items. In the specific example of this test program, you also have the problem that your binding is on the frame. At startup the frame has a size of 1x1 so it's a bit hard to click on. Moving the binding to the canvas will make this specific demo program work better.

Python scrollable widget in tk

So I have a little issue with a gui in tk (python, code below), the problem being that my scrollbar seems not to be linked to my canvas, though i think i asked it properly ... any hint ?
root = tk.Tk() # window
root_frame = tk.Frame(root) # main container
root_frame.pack()
container_frame = tk.Frame(root_frame) # specific container
container_frame.pack(fill="both")
inner_canvas = tk.Canvas(container_frame, width=100, height=100) # contains the widgets
inner_canvas.grid_propagate(False) # i heard this is necessary ...
inner_scrollbar = tk.Scrollbar(container_frame, command=inner_canvas.yview)
inner_canvas.configure(yscrollcommand=inner_scrollbar.set)
inner_canvas.pack(fill="both", side="left")
inner_scrollbar.pack(fill="y", side="right")
for k in range(100): # simulate the homemade widgets i want to add.
tk.Label(inner_canvas, text=str(k)+" row").grid(row=k, rowspan=1, columnspan=1)
root.mainloop()
The canvas scrollbar will only scroll widgets added to the canvas with create_window. It will not scroll widgets added to the canvas with pack, place or grid.

Python tkinter: issues resizing frames

I'm trying to resize a window in my GUI but one of my frames is getting left out and I'm not sure why. The window resizes fine horizontally, but when I try to resize vertically the frame with the button disappears. This is my first GUI so I'm sure there is something I'm missing...
from Tkinter import *
from ttk import *
class GUI(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.root = root
lbFrame = Frame(self.root)
nbFrame = Frame(self.root)
self.note = Notebook(nbFrame)
self.note.pack(fill=BOTH, expand=YES)
lbFrame.pack(side=LEFT, fill=BOTH, expand=YES)
nbFrame.pack(side=RIGHT, fill=BOTH, expand=YES)
self.make_file_viewer()
# Label
lblabel = Label(lbFrame, text='Files', background='#E8E8E8')
lblabel.pack(side=TOP, expand=YES, padx=10, pady=10)
# Listbox
self.lb = Listbox(lbFrame, height=49, borderwidth=0, font=('Purisa', 11), selectmode=EXTENDED)
self.lb.pack(side=BOTTOM, expand=YES, padx=10, pady=10)
def make_file_viewer(self):
fvwr = Frame(self.note)
dataFrm = Frame(fvwr)
btnFrm = Frame(fvwr)
dataFrm.pack(side=TOP, fill=BOTH, expand=YES)
btnFrm.pack(side=BOTTOM, fill=BOTH, expand=YES)
fvwr.config(borderwidth=2)
self.note.add(fvwr, text='File View')
# Label
self.lbl_fvwr_search = Label(dataFrm, text='Search Hits\t0', justify=LEFT)
self.lbl_fvwr_search.pack(side=TOP, anchor=W, expand=YES)
# Scrollbar
scrollbar_fvwr = Scrollbar(dataFrm)
scrollbar_fvwr.pack(side=RIGHT, fill=Y, expand=YES)
# Textbox
self.outputPanel_fvwr_text = Text(dataFrm, wrap='word', height=40, width=115, yscrollcommand=scrollbar_fvwr.set)
self.outputPanel_fvwr_text.pack(side=LEFT, fill=BOTH, expand=YES)
scrollbar_fvwr.config(command=self.outputPanel_fvwr_text.yview)
# Start button
viewBtn = Button(btnFrm, text='Start', width=8)
viewBtn.pack(anchor=W, expand=YES)
if __name__ == '__main__':
root = Tk()
app = GUI(root)
root.mainloop()
The absolute best thing you can do is to start over, and do your layout step-by-step. Start by creating the main areas, and make sure they resize properly. In your case, create the left and right sides. Again, get those two sides resizing properly with respect to each other.
Once you are done, focus on one section. Since you know the main section resizes properly, you only need to focus on the elements within that particular side. Again, break it down into pieces, and get those pieces working before tackling any widgets inside the main pieces.
When you do your layout this way, it's much easier to get the whole GUI working right, because you aren't trying to juggle the behavior of a half dozen widgets at once.
In your specific case, the root of the problem is that you have expand=YES for just about everything. As a general rule of thumb, you only want to set that to YES for one widget in an given parent window. For example, in your main window you want the right to expand but not the left (I'm guessing), and in the right window you want the text widget to expand but not the other widgets.
Set expand=NO for scrollbar_fvwr, self.lbl_fvwr_search, and btnFrm to get the right side to resize properly. For the left side, add fill=BOTH for self.lb, and expand=NONE for lblabel.

Categories