How to get these tkinter scrollbars working? - python-3.x

Hi cant get these scrollbars working even though this code comes from a very advanced user on this site I have tried everything. No errors they just dont show up
import tkinter as tk
#Make Window
root = tk.Tk()
root.geometry("612x417")
root.title("Exchange Rates")
root.resizable(0,0)
root.configure(background='lightgrey')
#End
#Create listboxes for currency selection
listbox1 = tk.Listbox(root, font="Helvetica 11 bold", height=3, width=10)
listbox2 = tk.Listbox(root, font="Helvetica 11 bold", height=3, width=10)
#Try to create a scroll bar
scrollbar1 = tk.Scrollbar(root, orient="vertical", command=listbox1.yview)
listbox1.configure(yscrollcommand=scrollbar1.set)
scrollbar2 = tk.Scrollbar(root, orient="vertical", command=listbox2.yview)
listbox2.configure(yscrollcommand=scrollbar2.set)
listbox1.place(x=300,y=50)
listbox2.place(x=300,y=125)
scrollbar3 = Scrollbar(root)
scrollbar3.pack(side="right", fill="y")
listbox = Listbox(root, yscrollcommand=scrollbar3.set)
listbox.pack()
scrollbar3.config(command=listbox.yview)
root.mainloop()

I don't know how you managed to run it without an error because you imported tkinter as tk but for listbox you put Listbox (not tk.Listbox) or for scrollbar3 you put Scrollbar (not tk.Scrollbar). Also they don't show up because you haven't packed/placed them! And... you have to use either place, pack or grid you can't use them together. You used .place() for your listbox1 and 2 but then you used .pack() for your scrollbar3 and listbox. Whatever you use first (here it's place) will work but the others just simply won't show up.

The below script should hopefully show you how Scrollbars work in a clear and concise way and how they are affected by the number of items entered into a listbox
from tkinter import *
class App:
def __init__(self, master):
self.master = master
self.top = Toplevel(self.master)
self.frame = Frame(self.top)
self.entry = Entry(self.master)
self.button = Button(self.master, text="Ok", command=self.command)
self.entry.pack()
self.button.pack()
self.top.withdraw()
self.frame.pack()
def command(self):
self.frame.destroy()
self.frame = Frame(self.top)
self.listbox = Listbox(self.frame)
self.scroll = Scrollbar(self.frame, orient="vertical", command=self.listbox.yview)
self.listbox.configure(yscrollcommand=self.scroll.set)
for i in range(int(self.entry.get())):
self.listbox.insert(END, "Col"+str(i))
self.frame.pack()
self.listbox.pack(side="left")
self.scroll.pack(side="left", expand=True, fill=Y)
self.top.deiconify()
root = Tk()
app = App(root)
root.mainloop()
Also please take into account that in order for us to review a problem and help you work through it, we need to be able to run and review the code in an easy and digestible fashion.

Hello people answering my own question, here is a working Listbox within a Frame with a Scrollbar.
import tkinter as tk
#Make Window
root = tk.Tk()
root.geometry("612x417")
root.title("Exchange Rates")
root.resizable(0,0)
root.configure(background='lightgrey')
#End
#Try to create a listbox with a scroll bar within a frame
#Create elements
frame = tk.Frame(root, bd=1, relief='sunken', width=150, height=300)
scrollbar = tk.Scrollbar(frame)
listbox = tk.Listbox(frame)
#Attach listbox to scrollbar
listbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=listbox.yview)
#Poulate listbox
for i in range(100):
listbox.insert('end', i)
#Pack elements
frame.pack(side='top')
scrollbar.pack(side='right', fill='y')
listbox.pack()
root.mainloop()

Related

Make multiple tk.Toplevel windows embedded/unified in main tk window

So I'm trying to create a program which uses multiple tk.Toplevel windows. The problem with this is, that all windows show up seperated as their "own App", so when you alt tab, you switch between the toplevel windows.
The pseudocode would look something like this:
import tkinter as tk
top_levels = {}
def open_toplevel():
top_level = tk.Toplevel(root)
top_level.geometry("300x200+0+0")
top_levels.update({f"toplevel{len(top_levels.keys())}" : top_level})
root = tk.Tk()
button = tk.Button(root, command= open_toplevel)
button.place(x=0, y=0)
root.mainloop()
So my question, is: is there a way to unify them into "one window"?
If you want all of them to unify into one window then tk.Frame is a better widget to use instead of tk.Toplevel
The purpose of tk.Toplevel is to create a new temporary window, not an extra part of the window. But frames are a really good way to organise stuff.
This code below creates new frame every time you click the button. This is just a simple example. You can also use grid for widgets in a frame. I also put a border so you can see where the frames are located.
from tkinter import *
def open_frame():
frame = Frame(root, highlightbackground="black", highlightthickness=2)
lbl1 = Label(frame, text=f"Frame {len(frames) + 1} label 1")
lbl2 = Label(frame, text=f"Frame {len(frames) + 1} label 2")
lbl1.pack()
lbl2.pack()
frame.pack(padx=5, pady=5)
frames.append(frame)
root = Tk()
frames = []
btn = Button(root, text="Open Frame", command=open_frame)
btn.pack()
root.mainloop()
I hope this solution is helpful
EDIT
Use this code here to move the frames:
from tkinter import *
def open_frame():
global frame, frames
frame = Frame(root, highlightbackground="black", highlightthickness=2)
lbl1 = Label(frame, text=f"Frame {len(frames) + 1} label 1")
lbl2 = Label(frame, text=f"Frame {len(frames) + 1} label 2")
lbl1.pack()
lbl2.pack()
frame.pack(padx=5, pady=5)
frame_number = len(frames)
lbl1.bind('<B1-Motion>', lambda event: MoveWindow(event, frame_number))
lbl2.bind('<B1-Motion>', lambda event: MoveWindow(event, frame_number))
frame.bind('<B1-Motion>', lambda event: MoveWindow(event, frame_number))
frames.append(frame)
labels.append(lbl1)
labels.append(lbl2)
def MoveWindow(event, frame_number):
global root, frames
root.update_idletasks()
f = frames[frame_number]
x = f.winfo_width()/2
y = f.winfo_height()*1.5
f.place(x=event.x_root-x, y=event.y_root-y)
root = Tk()
root.geometry("500x500")
frames = []
labels = []
btn = Button(root, text="Open Frame", command=open_frame)
btn.pack()
root.mainloop()

Scrollbar doesn't scroll widgets inside a frame which is inside a canvas

I have the canvas and the scrollbar are on the Tk.
I have a frame on the canvas.
I adding into this frame new frames with the widgets on it and I want to scroll these widgets.
The scrollbar doesn't scroll the widgets at all, and when I add widgets which go below the window than the scrollbar turn into gray and I can't use it at all.
I am new to tkinter and python. I just don't know yet what am I doing. I didn't try to make it with a class(Should I?). I tried to use the ttk, and looked around sites for a non class answers but none of them worked.
from tkinter import *
from tkinter.ttk import *
actor_number=0
global tk
def new_actor_button_command():
global menu_frame
global actor_number
global canvas
new_actor_frame=Frame(menu_frame,width=500,height=200)
new_actor_frame.grid(row=1+actor_number,column=0,pady=20)
actor_name_label=Label(new_actor_frame,text="Actor Name")
new_actor_frame.place(relx=0.0, rely=0.0, anchor=CENTER)
delete_actor_button=Button(new_actor_frame,text="Delete
Actor",command=delete_actor_button_command)
new_actor_frame.grid(row=1+actor_number,column=1)
actor_name_label.grid(row=2+actor_number,column=1)
delete_actor_button.grid(row=2+actor_number,column=2)
actor_number+=1
canvas.update_idletasks()
scrollbar.config(command=canvas.yview)
canvas.configure(scrollregion=canvas.bbox("all"))
def make_new_actor():
global canvas
global menu_frame
new_actor_button=Button(menu_frame,text="Add New
Actor",command=new_actor_button_command)
new_actor_button.grid(row=0,column=0)
def new_command():
global actor_number
actor_number=0
make_new_actor()
tk=Tk()
tk.geometry("1200x800")
menubar=Menu(tk)
filemenu=Menu(menubar,tearoff=0)
filemenu.config(font=("Verdana",16))
filemenu.add_command(label="New",font=("Verdana",16),command=new_command)
menubar.add_cascade(label="File", menu=filemenu)
global scrollbar
canvas=Canvas(tk,width=1000,height=1000)
scrollbar=Scrollbar(tk,orient="vertical",command = canvas.yview)
menu_frame=Frame(canvas,width=1000,height=1000)
canvas.create_window(0,0,window=menu_frame)
canvas.configure(yscrollcommand=scrollbar.set)
canvas.configure(scrollregion=canvas.bbox("all"))
canvas.place(relx=0.0, rely=0.0)
menu_frame.pack(side=LEFT,expand=True)
scrollbar.pack(side=RIGHT,fill=Y)
tk.config(menu=menubar)
tk.mainloop()
It should scroll the vidgets inside"menu_frame".
Scrolling Canvas is not easy.
It scroll items (if you add enough items in menu_frame) but it may need other changes in new_actor_frame
from tkinter import *
from tkinter.ttk import *
def new_actor_button_command():
new_actor_frame = Frame(menu_frame, width=500, height=200)
new_actor_frame.grid(row=actor_number, column=0)
actor_name_label = Label(new_actor_frame, text="Actor Name")
actor_name_label.grid(row=0, column=1)
delete_actor_button=Button(new_actor_frame,text="Delete Actor")#, command=delete_actor_button_command)
delete_actor_button.grid(row=0, column=2)
def new_command():
global actor_number
actor_number += 1
new_actor_button = Button(menu_frame, text="Add New Actor", command=new_actor_button_command)
new_actor_button.grid(row=actor_number, column=0)
def update_canvas(event=None):
canvas.configure(scrollregion=canvas.bbox("all"))
actor_number=0
tk = Tk()
tk.geometry("1200x800")
menubar = Menu(tk)
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="New", command=new_command)
menubar.add_cascade(label="File", menu=filemenu)
tk.config(menu=menubar)
canvas = Canvas(tk, background='white')#, width=1000, height=1000)
canvas.pack(side='left', fill='both', expand=True)
scrollbar = Scrollbar(tk, orient="vertical", command=canvas.yview)
scrollbar.pack(side='right', fill='y')
menu_frame = Frame(canvas)
canvas.create_window(0, 0, window=menu_frame, anchor='nw')
canvas.configure(yscrollcommand=scrollbar.set)
canvas.bind('<Configure>', update_canvas) # update when change size
tk.mainloop()

Adding padding query insert to Listbox

I have read a number of threads and other resources to try to find the correct way to handle this but I have not found anything that works with my application.
Here is what I am trying to accomplish.
When a query is completed and the insert of the data to a Listbox is done I cannot seem to get it to margin the data insert by 1 character space.
I am using pack() and I have read the tkinter manual for this and have tried each example available along with others found on various threads here.
The widget:
output = tkinter.Listbox(window_2, height = 20, font='Times 10',
width=42, bd=1, bg = '#FFD599', fg = '#9A0615', selectmode=SINGLE)
output.pack()
output.place(x=210, y=195)
I have tried padx and pady with pack() without success, although this works successfully with the Text widget. I have also attempted to use a few alternatives that I have found here on the site but all without success in margining the Listbox when the data is inserted.
Any advice?
pack's padx/pady and ipadx/ipady options don't affect the data that is inside the listbox. The listbox itself doesn't have any options to add an internal margin.
To get a margin around the inside of the listbox, what I normally do is give it a zero borderwidth and highlightthickness, and then place it in a frame with the same background color and let the frame be the border. You can then add any padding you want between the border and the listbox.
This is also convenient because you can put a scrollbar inside the frame, giving it the appearance that it is inside the listbox without actually being inside the listbox.
Example:
import tkinter as tk
root = tk.Tk()
root.configure(background="gray")
listbox_border = tk.Frame(root, bd=1, relief="sunken", background="white")
listbox_border.pack(padx=10, pady=10, fill=None, expand=False)
listbox = tk.Listbox(listbox_border, width=20, height=10,
borderwidth=0, highlightthickness=0,
background=listbox_border.cget("background"),
)
vsb = tk.Scrollbar(listbox_border, orient="vertical", command=listbox.yview)
listbox.configure(yscrollcommand=vsb)
vsb.pack(side="right", fill="y")
listbox.pack(padx=10, pady=10, fill="both", expand=True)
for i in range(100):
listbox.insert("end", "Item #{}".format(i))
root.mainloop()
here is a variation on the much appreciated answer by Bryan Oakley.
it uses ttk widgets instead of tk widgets
the scrollbar tracks your position in the list box when you scroll with the mouse
uses the oStyle.theme_use("clam") because it may look more modern...this can be commented out
'
import tkinter as tk
from tkinter import ttk
try: # allows the text to be more crisp on a high dpi display
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(1)
except:
pass
root = tk.Tk()
oStyle = ttk.Style()
oStyle.theme_use("clam")
oStyle.configure('LB.TFrame', bd=1, relief="sunken", background="white")
listbox_border = ttk.Frame(root, style='LB.TFrame')
listbox_border.pack(padx=4, pady=4, fill=None, expand=False)
vsb = ttk.Scrollbar(listbox_border)
vsb.pack(side="right", fill="y")
listbox = tk.Listbox(listbox_border, width=20, height=10, borderwidth=0,
highlightthickness=0, selectmode=tk.SINGLE,
activestyle=tk.NONE)
listbox.pack(padx=6, pady=6, fill="y", expand=True)
listbox.config(yscrollcommand=vsb.set)
vsb.config(command=listbox.yview)
for i in range(100):
listbox.insert("end", "Item #{}".format(i))
root.mainloop()
'
first of all to format chars in a tkinter listbox you need to use a fixed font and .format python funcion....;
So you can do something this
Press Load to load data in the listbox and pay attention to this line code
s = '{0:>8}{1:5}'.format(i[0],i[1])
self.list.insert(tk.END, s)
import tkinter as tk
RS = (('Apple',10),
('Banana',20),
('Peack',8),
('Lemon',6),)
class App(tk.Frame):
def __init__(self,):
super().__init__()
self.master.title("Hello World")
self.init_ui()
def init_ui(self):
self.pack(fill=tk.BOTH, expand=1,)
f = tk.Frame()
sb = tk.Scrollbar(f,orient=tk.VERTICAL)
self.list = tk.Listbox(f,
relief=tk.GROOVE,
selectmode=tk.BROWSE,
exportselection=0,
background = 'white',
font='TkFixedFont',
yscrollcommand=sb.set,)
sb.config(command=self.list.yview)
self.list.pack(side=tk.LEFT,fill=tk.BOTH, expand =1)
sb.pack(fill=tk.Y, expand=1)
w = tk.Frame()
tk.Button(w, text="Load", command=self.on_callback).pack()
tk.Button(w, text="Close", command=self.on_close).pack()
f.pack(side=tk.LEFT, fill=tk.BOTH, expand=0)
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=0)
def on_callback(self,):
for i in RS:
s = '{0:>8}{1:5}'.format(i[0],i[1])
self.list.insert(tk.END, s)
def on_close(self):
self.master.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()

Tkinter - How to center a Widget (Python)

I started with the GUI in Python and have a problem.
I've added widgets to my frame, but they're always on the left side.
I have tried some examples from the internet, but I did not manage it .
I tried .place, but it does not work for me. Can one show me how to place the widgets in the middle?
Code:
import tkinter as tk
def site_open(frame):
frame.tkraise()
window = tk.Tk()
window.title('Test')
window.geometry('500x300')
StartPage = tk.Frame(window)
FirstPage = tk.Frame(window)
for frame in (StartPage, FirstPage):
frame.grid(row=0, column=0, sticky='news')
lab = tk.Label(StartPage, text='Welcome to the Assistant').pack()
lab1 = tk.Label(StartPage, text='\n We show you helpful information about you').pack()
lab2 = tk.Label(StartPage, text='\n \n Name:').pack()
ent = tk.Entry(StartPage).pack()
but = tk.Button(StartPage, text='Press', command=lambda:site_open(FirstPage)).pack()
lab1 = tk.Label(FirstPage, text='1Page').pack()
but1 = tk.Button(FirstPage, text='Press', command=lambda:site_open(StartPage)).pack()
site_open(StartPage)
window.mainloop()
After you have created window, add:
window.columnconfigure(0, weight=1)
More at The Grid Geometry Manager
You are mixing two different Layout Managers. I suggest you either use The Grid Geometry Manager or The Pack Geometry Manager.
Once you have decided which one you would like to use, it is easier to help you :)
For example you could use the Grid Geometry Manager with two rows and two columns and place the widgets like so:
label1 = Label(start_page, text='Welcome to the Assistant')
# we place the label in the page as the fist element on the very left
# and allow it to span over two columns
label1.grid(row=0, column=0, sticky='w', columnspan=2)
button1 = Button(start_page, text='Button1', command=self.button_clicked)
button1.grid(row=1, column=0)
button2 = Button(start_page, text='Button2', command=self.button_clicked)
button2.grid(row=1, column=1)
This will lead to having the label in the first row and below the two buttons next to each other.

Python 3.3 TkInter window closes unexpectedly. Why?

I came up with the following code more as a reference to help me remember how to build GUI apps with TkInter. It runs great except when a click Button1 or any other widget whose command option is set to self.hello. As you can see in the code bellow, the hello function is like a place holder. While the button click works fine while running the script through IDLE, it simply causes the application to exit if you start the program by double-clicking the actual file test.pyw. My question is, why?
#Some guy somewhere
from tkinter import *
class Application:
def hello(self):
msg = messagebox.showinfo('Message Title','Message Body')
def __init__(self, parent):
parent.resizable(0,0)
parent.minsize(800, 400)
parent.title('Top Level')
# Global Padding pady and padx
pad_x = 0
pad_y = 0
# CASCADE MENU
# create a parent menu.
self.parentmenu1 = Menu(parent, tearoff=0)
#self.menubar1.add_command(label='Menu1', command=self.hello)
#create a child menu for parent menu.
self.parentmenu1_child1 = Menu(parent, tearoff=0)
self.parentmenu1_child1.add_command(label='Item1', command=self.hello)
self.parentmenu1_child1.add_command(label='Item2', command=self.hello)
self.parentmenu1_child1.add_command(label='Item3', command=self.hello)
#add child menu to parent menu.
self.parentmenu1.add_cascade(label='Menu1', menu=self.parentmenu1_child1)
#self.menubar1.add_separator()
# SINGLE MENU
# create a parent menu.
self.parentmenu1.add_command(label='Menu2', command=self.hello)
# SINGLE MENU
# create a parent menu.
self.parentmenu1.add_command(label='Menu3', command=self.hello)
# display the parent menu.
parent.config(menu=self.parentmenu1)
# Create controls
#create label
self.label1 = Label(parent, text='Label1')
#create textbox
self.textbox1 = Entry(parent)
#create button
self.button1 = Button(parent, text='Button1', command=self.hello)
#string variable to hold checkbox1 values.
self.str_checkbox1 = StringVar()
#create checkbox
self.checkbox1 = Checkbutton(parent, text='Checkbox1', variable=self.str_checkbox1, onvalue='on1', offvalue='off1')
#deselect checkbox1
self.checkbox1.deselect()
#string variable to hold checkbox2 values.
self.str_checkbox2 = StringVar()
#create checkbox
self.checkbox2 = Checkbutton(parent, text='Checkbox2', variable=self.str_checkbox2, onvalue='on2', offvalue='off2')
#deselect checkbox2
self.checkbox2.deselect()
#???? ..what sets the groupbox apart from others. primary key???!!
self.str_radiobutton1 = StringVar()
#command= parameter missing.
self.radiobutton1 = Radiobutton(parent, text='Radio 1', variable=self.str_radiobutton1, value='a')
self.radiobutton2 = Radiobutton(parent, text='Radio 2', variable=self.str_radiobutton1, value='b')
self.radiobutton1.select()
#create a list of options.
optionList = ('Option1', 'Option2', 'Option3')
#string variable to hold optionlist values.
self.str_optionmenu1 = StringVar()
#associate string variable with optionlist
self.str_optionmenu1.set(optionList[0])
#create optionmenu
self.optionmenu1 = OptionMenu(parent, self.str_optionmenu1, *optionList)
#create a frame
self.frame1 = Frame(parent)
#create a text.
self.textarea1 = Text(self.frame1, width=20, height=10)
#align text left and fill frame with it.
self.textarea1.pack(side=LEFT, fill=Y)
#create a scrollbar.
self.scrollbar1 = Scrollbar(self.frame1)
#align scrollbar right and fill frame with it.
self.scrollbar1.pack(side=RIGHT, fill=Y)
#what is going to be scrolled?
self.scrollbar1.config(command=self.textarea1.yview)
#set textarea scrollbar.
self.textarea1.config(yscrollcommand=self.scrollbar1.set)
#align frame left and fill.
self.frame1.pack(side=LEFT, fill=Y)
#create a frame
self.frame2 = Frame(parent)
#create a text.
self.listbox1 = Listbox(self.frame2, width=20, height=10, activestyle='none', selectmode=SINGLE)
#create a list of items.
optionList = ('Item1', 'Item2', 'Item3', 'Item4', 'Item5', 'Item6', 'Item7', 'Item8', 'Item9', 'Item10', 'Item11')
#add items from list to listbox
for item in optionList:
self.listbox1.insert(END, item)
#align text left and fill frame with it.
self.listbox1.pack(side=LEFT, fill=Y)
#create a scrollbar.
self.scrollbar2 = Scrollbar(self.frame2)
#align scrollbar right and fill frame with it.
self.scrollbar2.pack(side=RIGHT, fill=Y)
#what is going to be scrolled?
self.scrollbar2.config(command=self.listbox1.yview)
#set textarea scrollbar.
self.listbox1.config(yscrollcommand=self.scrollbar2.set)
#align frame left and fill.
self.frame2.pack(side=LEFT, fill=Y)
# Place controls inside of grid
self.label1.grid(row=0, column=0, padx=pad_x, pady=pad_y, sticky=W)
self.textbox1.grid(row=0, column=1, padx=pad_x, pady=pad_y, sticky=W)
self.button1.grid(row=1, column=0, padx=pad_x, pady=pad_y, sticky=W)
self.checkbox1.grid(row=1, column=1, padx=pad_x, pady=pad_y, sticky=W)
self.checkbox2.grid(row=1, column=2, padx=pad_x, pady=pad_y, sticky=W)
self.optionmenu1.grid(row=2, column=0, padx=pad_x, pady=pad_y, sticky=W)
self.frame1.grid(row=2, column=1, padx=pad_x, pady=pad_y, sticky=W)
self.radiobutton1.grid(row=3, column=0, padx=pad_x, pady=pad_y, sticky=W)
self.radiobutton2.grid(row=3, column=1, padx=pad_x, pady=pad_y, sticky=W)
self.frame2.grid(row=4, column=0, padx=pad_x, pady=pad_y, sticky=W)
if __name__ == '__main__':
parent = Tk()
app = Application(parent)
parent.mainloop()
Alright. Apparently tkMessageBox has been renamed to messagebox in Python 3.x. Also this
module is not available in tkinter so even though a developer might use:
from tkinter import *
..he/she would still need to:
from tkinter import messagebox

Resources