How to access entries beyond the window size in Tkinter - python

I am creating a form with 40 labels and entries. The problem is that I can enter till 20 after that the window size reaches the maximum and I cannot see entries below it. How do I integrate a scrolling option in Tkinter main window? I know that scrollbar cannot be integrated into main window and only to widgets. I want something so that I can either scroll using mouse or arrow keys or anything to see below content. Below is my code:
from Tkinter import *
root = Tk()
root.title('test')
root.geometry("400x400")
for i in range(40):
Label(root, text="Field {} ".format(i)).grid(row=i,column=0)
value=Entry(root).grid(row=i,column=1)
root.mainloop()
Output image

ListBox
Scrollbars are almost always used in conjunction with Listbox, Canvas or Text widget. To connect a vertical scrollbar to one of these widgets you have to do two things:
Set the widget’s yscrollcommand callbacks to the set method of the scrollbar.
Set the scrollbar’s command to the yview method of the widget.
Example
from tkinter import *
master = Tk()
scrollbar = Scrollbar(master)
scrollbar.pack(side=RIGHT, fill=Y)
listbox = Listbox(master, yscrollcommand=scrollbar.set)
for i in range(40):
listbox.insert(END, Label(master, text=f"Field {i} "))
listbox.insert(END, Entry(master))
listbox.pack(side=LEFT, fill=BOTH)
scrollbar.config(command=listbox.yview)
mainloop()

Related

Tkinter scrollbar is not scrolling

As part of a tkinter app I'm building using Python 3.8, I need a particular tab in a Notebook to be scrollable. The notebook needs to remain at a fixed size, but the problem is that there will be cases in which the contents of the tab will exceed the size of the notebook.
The scrollbar appears as it should, but scrolling appears to have no effect on the contents of the tab. It looks like it thinks it's scrolling something but I do not know what. Here's an isolated example of a tab with a scrollbar which has no effect:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
tabs = ttk.Notebook(root, width=200, height=650)
tab_options = tk.Frame(tabs)
tabs.add(tab_options, text="Options")
main_frame = tk.Frame(tab_options)
main_frame.pack()
canvas = tk.Canvas(main_frame)
canvas.pack(side="left",fill="both",expand=1)
scrollbar = ttk.Scrollbar(main_frame,orient="vertical",command=canvas.yview)
scrollbar.pack(side="right", fill="y",expand=1)
lf_options = tk.Frame(canvas)
lf_options.pack()
canvas.configure(yscrollcommand=scrollbar.set)
canvas.configure(scrollregion=(0,0,200,1000))
for i in range(50):
ttk.Label(lf_options, text=str(i)).pack()
tabs.pack()
root.mainloop()
I imagine it's something to do with how I'm hooking up the frames to the canvas but I cannot for the life of me get it to work. I've seen suggestions about setting scrollregion to
canvas.bbox("all")
but I don't understand how to associate that with the maximum height that can be displayed, i.e. the height of the notebook itself. Using that as the scrollregion also just makes the scrollbar unscrollable.
I know there are many similar questions on here, but I have not found any of those examples to work in this case.
Any advice would be greatly appreciated. Thanks!
It seems more logical to use a tk.Listbox for this purpose, see below for example an edited version of your code. Here the scrollbar works just as expected!
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
tabs = ttk.Notebook(root, width=200, height=650)
tab_options = tk.Frame(tabs)
tabs.add(tab_options, text="Options")
listbox = tk.Listbox(tab_options)
for i in range(50):
listbox.insert(tk.END, f"Number {i}")
listbox.pack(side="left", fill="both", expand=1)
scrollbar = ttk.Scrollbar(tab_options, orient="vertical", command=listbox.yview)
scrollbar.pack(side="right", fill="y", expand=1)
listbox.configure(yscrollcommand=scrollbar.set)
tabs.pack()
root.mainloop()

Creating a new window in tkinter that has the same widgets from the root window

I am wanting to create a tkinter window where when I click a button widget it opens a new window, showing all the widgets, exactly the same, from the root/original window. Essentially creating a second instance of the root window, where the application can have multiple users, using the same GUI, in different windows.
Any help is appreciated.
An example of one of my widgets:
summary_output = Text(
master=window,
height=8,
width=78,
bg="gray95",
borderwidth=2,
relief="groove",
font=("Arial", 12))
My window layout
window = Tk()
window.title("Data Viewer")
window.geometry("750x950")
window.configure(bg='white')
window.iconphoto(False, tk.PhotoImage(file='icon.png'))
I have this but cant seem to place the widgets from the root window:
def new_window():
newWindow = Toplevel(window)
newWindow.geometry("750x950")
newWindow.configure(bg='white')
newWindow.iconphoto(False, tk.PhotoImage(file='icon.png'))
upload_button.place(x=20, y=560)
mainloop()
Is their anyway to change the master to be any window?
Edit:
from tkinter import *
class StaticFrame(Frame):
def __init__(self,master,*args,**kwargs):
Frame.__init__(self,master,*args,**kwargs)
# All your widgets
Label(self,text='This is a reusable frame',font=(0,17)).place(x=0, y=0)
Button(self,text='Click me for nothing').pack()
Label(self,text='End of page').pack()
upload_button = Button(
self,
text="Edit Data",
fg="DodgerBlue4",
font=("Graph Type", 15),
height=1, width=12,
borderwidth=2,
relief="groove")
upload_button.place(x=20, y=50)
root = Tk() # First window
top = Toplevel(root) # Second window
root.geometry("750x968")
StaticFrame(root).pack() # Put the frame on the first window
StaticFrame(top).pack() # Put the frame on the second window
root.mainloop()
Result:
The concept used here is simple, create a "custom frame" that we will put onto these new windows, so that it will create the exact same frame, and widgets within it, inside different windows.
from tkinter import *
class StaticFrame(Frame):
def __init__(self,master,*args,**kwargs):
Frame.__init__(self,master,*args,**kwargs)
# All your widgets
Label(self,text='This is a reusable frame',font=(0,17)).pack()
Button(self,text='Click me for nothing').pack()
Label(self,text='End of page').pack()
root = Tk() # First window
top = Toplevel(root) # Second window
StaticFrame(root).pack() # Put the frame on the first window
StaticFrame(top).pack() # Put the frame on the second window
root.mainloop()
Very simple to code and has been explained with comments, if you do not know what classes and inheritance is then first do go through those. There are variety of other methods that come onto mind when I read this question, like even having an option database and storing the widgets in a list and recreating it based on its order, but this seems to be the easiest in a scratch.

Python's Tkinter Entry not appearing on window

I am trying to make a window that would take an input through an entry and that would be either a web address or ip address and i would use a loop to update the text of a label to show the current ping every second. But I'm stuck at the very beginning because my entry would not appear on my window. Here is my code:
import tkinter as tk
from tkinter import *
window = tk.Tk()
window.title("Server Status")
window.geometry('400x600')
window.resizable(0,0)
canvas = tk.Canvas(window,height=600,width=1000,bg='#263D42')
canvas.pack()
txtf=tk.Entry(window, width=10)
txtf.pack()
window.mainloop()
Where am I going wrong? I have tried it with several changes but still cant get it to appear there. Any help would be appreciated.
Thanks.
Your entry is below the canvas, but because (1) your window geometry specifies a smaller size than that requested for the canvas, and (2) you set it to be non resizable, you can never access it.
Choose how to resolve this conflict; the example below sets the size of the canvas, and lets the window resize to enclose all its widgets.
import tkinter as tk
window = tk.Tk()
window.title("Server Status")
canvas = tk.Canvas(window, height=600, width=1000, bg='#263D42')
canvas.pack()
txtf = tk.Entry(window, width=10)
txtf.pack()
window.mainloop()

Is there a tkinter equivalent for Perl/Tk's Scrolled pseudo-widget?

Perl/Tk has a pseudo-widget Tk::Scrolled that takes as its argument another widget and adds a corresponding scrollbar according to options (where to put it in relation to the widget and if to show at all if there is nothing to scroll). For example, to have a listbox with a scrollbar to the right that disappears if the listbox can display all entries you just have to say:
my $Listbox = $MW->Scrolled ('Listbox', -scrollbars => 'oe');
Has tkinter (3.3.2) some equivalent functionality?
Tkinter has a Scrollbar class that can be used to wrap widgets in a scrollbar. It may not be as concise to configure as perl, but you can set it up to do what you are asking for without too much hassle.
Here are some examples of the scrollbar in use:
from Tkinter import *
root = Tk()
scrollbar = Scrollbar(root)
scrollbar.pack(side=RIGHT, fill=Y)
listbox = Listbox(root)
listbox.pack()
for i in range(100):
listbox.insert(END, i)
# attach listbox to scrollbar
listbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=listbox.yview)
mainloop()

Resize Tkinter Listbox widget when window resizes

I'm new to Tkinter, and I've got a Listbox widget that I'd like to automatically-resize when changing the main window's size.
Essentially I would like to have a fluid height/width Listbox. If someone can point me to some documentation or provide a bit a code / insight, I'd appreciate it.
You want to read up on the geometry managers pack and grid, which lets you place widgets in a window and specify whether they grow and shrink or not. There's a third geometry manager, place, but it's not used very often.
Here's a simple example:
import tkinter as tk
root = tk.Tk()
scrollbar = tk.Scrollbar(root, orient="vertical")
lb = tk.Listbox(root, width=50, height=20, yscrollcommand=scrollbar.set)
scrollbar.config(command=lb.yview)
scrollbar.pack(side="right", fill="y")
lb.pack(side="left",fill="both", expand=True)
for i in range(0,100):
lb.insert("end", "item #%s" % i)
root.mainloop()
If you wish to use grid instead of pack, remove the two lines that call pack and replace them with these four lines:
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
scrollbar.grid(row=0, column=1, sticky="ns")
lb.grid(row=0, column=0, sticky="nsew")
Note that with grid you have to take the extra step to configure the weight for the row and column that contains the listbox, otherwise tkinter won't allocate any extra space to the widget.
The two main ways to allow a listbox to stretch when the window is resized are using the .pack() or .grid() methods.
SPECS:
Windows 7, Python 3.8.1, tkinter version: 8.6
.pack()
I found the easiest way to do this is by using the .pack() method, and utilizing the fill= & expand=True options.
import tkinter as tk
root=tk.Tk() #Creates the main window
listbox=tk.Listbox(root) #Create a listbox widget
listbox.pack(padx=10,pady=10,fill=tk.BOTH,expand=True) #fill=tk.BOTH, stretch vertically and horizontally
#fill=tk.Y, stretch vertically
#fill=tk.X, stretch horizontally
If your listbox is placed in a frame, the frame will also need to use the fill= & expand=True options.
import tkinter as tk
root=tk.Tk()
frame1=tk.Frame(root)
frame1.pack(fill=tk.BOTH, expand=True)
listbox=tk.Listbox(frame1)
listbox.pack(padx=10,pady=10,fill=tk.BOTH,expand=True)
.grid()
The alternative technique is to use the .grid() method and utilize thesticky= option. In addition, you will need to configure the row and column that the listbox resides in.
import tkinter as tk
root=tk.Tk() #create window
root.columnconfigure(0,weight=1) #confiugures column 0 to stretch with a scaler of 1.
root.rowconfigure(0,weight=1) #confiugures row 0 to stretch with a scaler of 1.
listbox=tk.Listbox(root)
listbox.grid(row=0,column=0,padx=5,pady=5,sticky='nsew')
The sticky option causes the listbox to stick to the "North" (Top), "South" (Bottom), "East" (Right), and "West" (Left) sides of the cell as it is stretched.
If your listbox is placed within a frame, you will need to configure the column and row that the frame is in, along with configure the column and row that the listbox is in.
import tkinter as tk
root=tk.Tk() #create window
root.columnconfigure(0,weight=1)
root.rowconfigure(0,weight=1)
frame1=tk.Frame(root)
frame1.grid(row=0,column=0,sticky='nsew')
frame1.columnconfigure(0,weight=1)
frame1.rowconfigure(0,weight=1)
listbox=tk.Listbox(frame1)
listbox.grid(row=0,column=0,padx=5,pady=5,sticky='nsew')
.pack() & .grid()
Now there is one other technique, but some people frown on it. The third technique is to utilize the .pack() method and .grid() method in the same script. You can mix different geometry management method in the same script as long as only a one management type is used per container. You can see an example of this below.
import tkinter as tk
root=tk.Tk() #create window
frame1=tk.Frame(root) #container: root
frame1.pack(fill=tk.BOTH,expand=True)
frame1.columnconfigure(0,weight=1)
frame1.rowconfigure(0,weight=1)
frame1.rowconfigure(1,weight=1)
listbox=tk.Listbox(frame1) #container: frame1
listbox.grid(row=0,rowspan=2,column=0,padx=5,pady=5,sticky='nsew')
btn1=tk.Button(frame1,text='Demo1') #container: frame1
btn1.grid(row=0,column=1, padx=5, pady=5)
btn2=tk.Button(frame1,text='Demo2') #container: frame1
btn2.grid(row=1,column=1, padx=5, pady=5)
frame2=tk.Frame(root) #container: root
frame2.pack()
btn3=tk.Button(frame2,text='Demo3') #container: frame2
btn3.grid(row=0,column=0)
You can see above that the frames used .pack() while the listbox and buttons used .grid(). This was possible because the frames resided within the root container, while the listbox and buttons resided within their respective frames.
To check you tkinter version use:
import tkinter as tk
print(tk.TkVersion)
If you would like to learn about the differences between fill and expand, please see the following link.
https://effbot.org/tkinterbook/pack.htm

Categories