How to use .grid with a scrollbar in a tkinter window? - python-3.x

I have a tkinter window with several images that use grid. I tried to grid a scrollbar to the right side of the window but don't know what options to use because "side = right", "fill = Y" cannot be used inside grid. Any suggestions?

Grid is row / column oriented. So each widget needs its row and column in the geometry manager set. Here is a demo that answers the question posed.
# create a widget and scrollbar
import tkinter as tk
root = tk.Tk()
text = tk.Text(root)
vs = tk.Scrollbar(root, command=text.yview)
text.configure(yscrollcommand=vs.set)
# set the row, column and expansion for each widget
text.grid(row=0, column=0, sticky='news')
vs.grid(row=0, column=1, sticky='news')
# now make the grid manager expand to fill the available space in root.
# making 0, 0 expand to use all the spare space and 0,1 be fixed
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
root.mainloop()
If you are managing a set of images on label widgets then put them in a frame to separate out the geometry management of those. You might want to consider if you should
put them into a canvas instead which has quite good scrolling support.

Related

The bar in my Scrollbar is missing from my python program using Tkinter GUI

I want to make a window to display data stored inside dictionaries. I have created a canvas, scrollbar and a frame inside the canvas to place my Text widgets to display the information. When I execute my code the layout is just as I want it, but the bar is missing from the scroll bar and I can't figure out why. Here is my code for the GUI portion:
root = Tk()
# create top part
top=Frame(root)
# create a canvas to scroll
holder = Canvas(top)
# create scroll bar
scroll = Scrollbar(top, orient=VERTICAL, command=holder.yview)
# configure scrollbar for canvas
holder.configure(yscrollcommand=scroll.set)
holder.bind('<Configure>', lambda e: holder.configure(scrollregion=holder.bbox("all")))
# create frame for content inside canvas and add it to the canvas
content = Frame(holder, relief=RAISED)
holder.create_window(0, 0, window=content, anchor='nw')
# create bottom part
bottom = Frame(root, relief=SUNKEN)
root.rowconfigure(0, weight=18)
root.rowconfigure(1, weight=1, minsize=50)
root.columnconfigure(0, weight=1)
holder.pack(side=LEFT, fill=BOTH, expand=1)
scroll.pack(side=RIGHT, fill=Y)
content.pack(fill=X)
top.grid(row=0, column=0, sticky='NSEW')
bottom.grid(row=1, column=0, sticky='NSEW')
num=0
for site in sites:
temp=Text(content, height = 5)
temp.configure(state= DISABLED)
temp.pack(fill=X, side=TOP, padx= 5, pady= 5)
siteBoxes.append(temp)
num += 1
root.mainloop()
here is a screenshot of what the result looks like
Program GUI screenshot
there are that many text boxes there just to test the scrollbar, the data display within the Text widgets haven't been written yet.
I found the culprit, it was the line content.pack(fill=X), since I already have it in the window of the canvas this line of code was causing the issue. How ever now the Text widget no longer spans all of the horizontal space, how would I fix this issue while still using .create_window()?

Why is Python Tkinter grid method separating the buttons

I am trying to make a game using python 3.8, where I have 1 canvas/frame and 2 buttons. I want the buttons to be right next to each other but whenever I run the code, there is always white space in between the 2 buttons. I tried using pack() but when you click on the buttons, it makes the buttons go above the canvas/frame instead of to the right of it.
My code:
from tkinter import *
# windows, canvas, and frames
root = Tk()
WatchRun = Canvas(root, bg="green", width=600, height=500)
WatchRun.grid(row=0, column=0, rowspan=25)
Upgrade = Frame(root, bg="yellow", width=600, height=500)
Upgrade.grid_forget()
# button functions
def show_upgrade(widget, widget2):
global upgradeBtn
global WatchRunBtn
widget.grid_forget()
widget2.grid(row=0, column=0, rowspan=25)
def show_watchrun(widget, widget2):
global upgradeBtn
global WatchRunBtn
widget.grid_forget()
widget2.grid(row=0, column=0, rowspan=25)
# variables and buttons
distance = 0
started = 0
money = 0
startImage = PhotoImage(file='start.png')
stopImage = PhotoImage(file='stop.png')
upgradeBtn = Button(root, text="Upgrades", width=9, command=lambda: show_upgrade(WatchRun, Upgrade))
upgradeBtn.grid(row=0, column=1)
WatchRunBtn = Button(root, text="Watch run", width=9, command=lambda: show_watchrun(Upgrade, WatchRun))
WatchRunBtn.grid(row=1, column=1)
#loop
root.mainloop()
It appears you're trying to use rowspan to try to force the widgets apart. That's not a good solution.
While there are multiple ways to solve the problem of the buttons not being together, the one I recommend in this specific case is to treat your GUI as if it had three rows (or maybe four, depending on what you want to happen when you resize the window).
Row 0 holds the first button, row 1 holds the second, and row 2 takes up the rest of the GUI. Then, the canvas can span all three rows.
So, start by adding a non-zero weight to the third row so that all unallocated space goes to it.
root.grid_rowconfigure(2, weight=1)
Next, add your buttons to rows 0 and 1:
upgradeBtn.grid(row=0, column=1)
WatchRunBtn.grid(row=1, column=1)
And finally, have your canvas span all three rows. It will force the window to grow larger, with all extra space being given to the third row. This allows the first two rows to retain their natural small height.
WatchRun.grid(row=0, column=0, rowspan=3)
An arguably better solution involves using pack and an extra frame or two. The UI seems to clearly have two logical sections to it: a canvas on the left and a column of buttons on the right. So, you can create one frame for the left and one for the right. Then, put the buttons in the right frame and the canvas in the left frame.
See my comments that indicates the changes in the code. If you have questions left on it, let me know. You may find this Q&A helpfull as well. PEP 8
import tkinter as tk #no wildcard imports
#free functions under imports to ensure function is defined
def show_frame(widget, widget2):
'''common function for upgrade_btn and watchrun_btn
- packs the appropiated widget in the left_frame'''
widget.pack_forget()
widget2.pack()
distance = 0
started = 0
money = 0
#variable names lowercase
root = tk.Tk()
#split window in two containers/master/frames
left_frame = tk.Frame(root)
right_frame= tk.Frame(root)
#leftframe content
watchrun = tk.Canvas(left_frame, bg="green", width=600, height=500)
upgrade = tk.Frame(left_frame, bg="yellow", width=600, height=500)
#rightframe content
upgrade_btn = tk.Button(right_frame, text="Upgrades", width=9, command=lambda: show_frame(watchrun, upgrade))
watchrun_btn = tk.Button(right_frame, text="Watch run", width=9, command=lambda: show_frame(upgrade, watchrun))
#geometry management
left_frame.pack(side=tk.LEFT)
right_frame.pack(side=tk.RIGHT,fill=tk.Y)
watchrun.pack()
upgrade_btn.pack()
watchrun_btn.pack()
root.mainloop()

Why is the canvas scrollbar disabled in a tkinter window?

I have a problem with a tkinter application that is being made using python 3. I tried to create a canvas widget, without using a frame container and I tried to add two scrollbar to my canvas, one vertical and another orizontal, here is the code:
class drawCavnas(object):
def __init__(self, master, width=500, height=500):
''' build the canvas object '''
# class attributes
self.master = master
self.cWidth = width
self.cHeight = height
# creating the canvas object
self.canvas = tk.Canvas(self.master, width=self.cWidth, height=self.cHeight, bg="green")
self.canvas.grid(row=0, column=1, sticky="nwes")
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
# creating the scrolling
self.scroll_x = tk.Scrollbar(self.master, orient="horizontal", command=self.canvas.xview)
self.scroll_x.grid(row=1, column=1, sticky="ew")
self.scroll_y = tk.Scrollbar(self.master, orient="vertical", command=self.canvas.yview)
self.scroll_y.grid(row=0, column=2, sticky="ns")
self.canvas.configure(yscrollcommand=self.scroll_y.set, xscrollcommand=self.scroll_x.set)
where the master is the window created using tk.Tk(). The problem here is that once I've ran the program and once the window is created the canvas toolbars' are disabled and I can't interact with them. I tried to resize the window but the canvas resize as well. So do you have a solution?
Why is the canvas scrollbar disabled in a tkinter window?
You must tell what part of the larger virtual canvas you want to be scrollable. You've set the scroll region to be bounded by the objects in the canvas (self.canvas.configure(scrollregion=self.canvas.bbox("all"))). However, you've done that before you draw anything in the canvas so tkinter thinks there is nothing to scroll.
If you have a 500x500 canvas, but want to be able to draw on some larger area, you can hard-code the scrollregion.
For example, this will let you scroll around in a 2000x2000 area:
self.canvas.configure(scrollregion=(0,0,2000,2000)

Why the tkinter window does not open when using the method "grid()"?

from tkinter import *
# ==================================================Settings=======================================================
root = Tk()
root.title("Video Youtube Downloader") # set up the title and size.
root.configure(background='black') # set up background.
root.minsize(800, 500)
root.maxsize(800, 500)
# ==================================================Frames=======================================================
top = Frame(root, width=800, height=50, bg='yellow').pack(side=TOP)
bottom = Frame(root, width=800, height=50, bg='red').pack(side=BOTTOM)
left = Frame(root, width=550, height=450, bg='black').pack(side=LEFT)
right = Frame(root, width=250, height=450, bg='blue').pack(side=RIGHT)
# ==================================================Buttons=======================================================
btn_clear_url = Button(right, text="Clear Url", font=('arial', 10, 'bold')).grid(row=1, columns=1)
I am trying to add buttons to the right Frame, but for some reason when I run the program the IDE shows that it is running but there is
no window. When I delete .grid(row=1, columns=1) the window appears.
How can I fix this bug, and add btn_clear_url button to the right Frame?
First of all, you need to invoke Tk's mainloop at the end of your code (you can see here why).
Another problem is chaining method calls like that. You're actually assigning return value of the last call to the variable, which is None in case of grid() and pack() - therefore, all your variables end up having None as the value. You need to separate widget instantiating call and grid or pack call and put each on its own line.
Other than that, you're setting both minsize and maxsize to the very same size - if you're really just trying to make your window not resizable, set the size with:
root.geometry('800x500') # for instance
...and after that configure resizable attribute:
root.resizable(width=False, height=False)
Also, I suggest you get rid of from tkinter import *, since you don't know what names that imports. It can replace names you imported earlier, and it makes it very difficult to tell where names in your program are supposed to come from. Use import tkinter as tk instead.
When you place widget in a tk window you cannot use grid and pack at the same time in the same window, so you use should use pack () instead of grid()
The problem starts with this line of code:
right = Frame(root, width=250, height=450, bg='blue').pack(side=RIGHT)
With that, right is set to None because .pack(side=RIGHT) returns None.
Next, you do this:
btn_clear_url = Button(right, ...)).grid(row=1, columns=1)
Because right is None, it's the same as Button(root, ...). Since you are already using pack for a widget in root, you can't also use grid.
The solution -- and best practice -- is to separate widget creation from widget layout:
top = Frame(root, width=800, height=50, bg='yellow')
bottom = Frame(root, width=800, height=50, bg='red')
left = Frame(root, width=550, height=450, bg='black')
right = Frame(root, width=250, height=450, bg='blue')
top.pack(side=TOP)
bottom.pack(side=BOTTOM)
left.pack(side=LEFT)
right.pack(side=RIGHT)
With the above, right will be properly set to the frame instance, and adding a widget to right and using grid will no longer fail.

Can you fit multiple buttons in one grid cell in tkinter?

I'm making a table, and the grid of the table is going to be filled with buttons, Is it possible to fit more than one button in a grid space?
Yes you can. Put a frame inside the cell, and then you can put whatever you want inside the frame. Inside the frame you can use pack, place or grid since it is independent from the rest of the widgets.
For example:
import Tkinter as tk
root = tk.Tk()
l1 = tk.Label(root, text="hello")
l2 = tk.Label(root, text="world")
f1 = tk.Frame(root)
b1 = tk.Button(f1, text="One button")
b2 = tk.Button(f1, text="Another button")
l1.grid(row=0, column=0)
l2.grid(row=0, column=1)
f1.grid(row=1, column=1, sticky="nsew")
b1.pack(side="top")
b2.pack(side="top")
root.mainloop()
#jasonharper already provided the answer, but here's some code to go along with it.
This is just a random example with a bunch of buttons / frames using grid / pack. The pack for the buttons was arbitrary you could have used grid instead Each grid section has a random padx to show that it's in a different column and each different column within the grid contains multiple buttons
import tkinter as tk
root = tk.Tk()
#Now you want another frame
for i in range(5):
gridframe = tk.Frame(root)
for j in range(3):
tk.Button(gridframe, text="%d%d" % (i, j)).pack(side=tk.LEFT)
gridframe.grid(row=0, column=i, padx=20)
root.mainloop()

Resources