How to add a scrollbar to a window with tkinter? - python

I have a tkinter program:
import urllib.request
from tkinter import *
root = Tk()
root.iconbitmap(default='icon.ico')
root.wm_title('Got Skills\' Skill Tracker')
frame = Frame(width="500",height="500")
frame.pack()
def show():
name = "zezima"
page = urllib.request.urlopen('http://hiscore.runescape.com/index_lite.ws?player=' + name)
page = page.readlines()
skills = []
for line in page:
skills.append([line.decode("utf-8").replace("\n", "").split(",")])
skills = skills[0:25]
for item in skills:
toPrint = item[0][0],"-",item[0][1],"-",item[0][1],"\n"
w = Message(frame, text=toPrint)
w.pack()
menu = Menu(root)
root.config(menu=menu)
filemenu = Menu(menu)
menu.add_cascade(label="Commands", menu=filemenu)
filemenu.add_command(label="Show Skills", command=show)
root.mainloop()
When I run the above script, it shows this (which is good):
alt text http://img708.imageshack.us/img708/8821/tkinter1.png
When I click Commands > Show Skills, it turns into this. (Linked because it's tall.) It shows the right thing, but...I can imagine you see the problem.
Two questions:
-How do I add a scrollbar to the frame, and keep the frame a fixed size? (Ideally, keep the size of the first image, add the output of show(), add a scrollbar to the first image of the program.)
-With the following code:
for item in skills:
toPrint = item[0][0],"-",item[0][1],"-",item[0][2],"\n"
w = Message(frame, text=toPrint)
w.pack()
Is that the best way to output what I'm outputting? The list (skills) looks like [[1,2,3],[4,5,6]..], and I want to display 1-2-3 on a line, 4 - 5 - 6 on a line, etc.
But, I don't want that extra line in between them like there is now, and I was wondering if how I did it is the best way to go about doing it.

To add the scroll bars, use tkinter.tix.ScrolledWindow.
To remove extra space drop the extra "\n" and display a string, not a tuple. Here is the complete code:
import urllib.request
from tkinter import *
from tkinter.tix import *
root = Tk()
root.iconbitmap(default='icon.ico')
root.wm_title('Got Skills\' Skill Tracker')
frame = Frame(width="500",height="500")
frame.pack()
swin = ScrolledWindow(frame, width=500, height=500)
swin.pack()
win = swin.window
def show():
name = "zezima"
page = urllib.request.urlopen('http://hiscore.runescape.com/index_lite.ws?player=' + name)
page = page.readlines()
skills = []
for line in page:
skills.append([line.decode("utf-8").replace("\n", "").split(",")])
skills = skills[0:25]
for item in skills:
toPrint = item[0][0],"-",item[0][1],"-",item[0][1]
w = Message(win, text=' '.join(toPrint), width=500)
w.pack()
menu = Menu(root)
root.config(menu=menu)
filemenu = Menu(menu)
menu.add_cascade(label="Commands", menu=filemenu)
filemenu.add_command(label="Show Skills", command=show)
root.mainloop()

Here's a class for scrolling frames. Just pass the window object as traditional tkinter style and use obj.frame as window for new widgets.
class ScrollableFrame:
"""
# How to use class
from tkinter import *
obj = ScrollableFrame(master,height=300 # Total required height of canvas,width=400 # Total width of master)
objframe = obj.frame
# use objframe as the main window to make widget
"""
def __init__ (self,master,width,height,mousescroll=0):
self.mousescroll = mousescroll
self.master = master
self.height = height
self.width = width
self.main_frame = Frame(self.master)
self.main_frame.pack(fill=BOTH,expand=1)
self.scrollbar = Scrollbar(self.main_frame, orient=VERTICAL)
self.scrollbar.pack(side=RIGHT,fill=Y)
self.canvas = Canvas(self.main_frame,yscrollcommand=self.scrollbar.set)
self.canvas.pack(expand=True,fill=BOTH)
self.scrollbar.config(command=self.canvas.yview)
self.canvas.bind('<Configure>', lambda e: self.canvas.configure(scrollregion = self.canvas.bbox("all")))
self.frame = Frame(self.canvas,width=self.width,height=self.height)
self.frame.pack(expand=True,fill=BOTH)
self.canvas.create_window((0,0), window=self.frame, anchor="nw")
self.frame.bind("<Enter>", self.entered)
self.frame.bind("<Leave>", self.left)
def _on_mouse_wheel(self,event):
self.canvas.yview_scroll(-1 * int((event.delta / 120)), "units")
def entered(self,event):
if self.mousescroll:
self.canvas.bind_all("<MouseWheel>", self._on_mouse_wheel)
def left(self,event):
if self.mousescroll:
self.canvas.unbind_all("<MouseWheel>")

Related

Making a window UI scroll down and deleting labels from frame

I am making a random generator for my friends and I'm stuck trying to make a scroll down option. So if you generate more the window can show, a scroll down window should be possible. But I can't seem to get any to work. I've tried many online tutorials.
And my second issue with my code is that I can't clear the generated labels from the window. I got it working that it expands the window.
from cProfile import label
from pickle import FRAME
import random
import tkinter as tk
from tkinter import BOTH, DISABLED, LEFT, RIGHT, VERTICAL, Y, Frame, Label, filedialog, Text
import os
from tkinter import ttk
from tkinter.font import NORMAL
from tkinter.messagebox import YES
root = tk.Tk()
root.title('guesser')
#Pelin arvonta ohjelma !
def delete():
for child in root.children.values():
info = child.grid_info()
if info['column'] == 0:
child.grid_forget()
def arvonta():
global label
list1 = []
lista = ["Valorant","Rainbow","Vampire: The masquerade","Playerunknown's battlegrounds","Fortnite","Left 4 Dead 2","Counter strike Global offensive","Realm roayale","Black ops 1 zombies/multiplayer","Black ops 2 zombies/multiplayer","Black ops 3 zombies/multiplayer"]
numero = random.randint(0, 10)
hahmo = (lista[numero])
list1.append(hahmo)
for app in list1:
label = tk.Label(frame, text=app, bg="red",font=('Helvetica',20))
label.pack()
def valorant():
list2 = []
lista2 = ["Brimstone","Viper","Omen","Killjoy","Cypher","Sova","Sage","phoenix","Jett","Reyna","Raze","Raze","Breach","Skye","Yoru","Astra","Kay/o","Chamber","Neon","Fade"]
numero = random.randint(0, 19)
randomValorantagent=(lista2[numero])
list2.append(randomValorantagent)
for app in list2:
label = tk.Label(frame, text=app, bg="red",font=('Helvetica',20))
label.pack()
def quitter():
quit()
canvas = tk.Canvas(root,height=700,width=700,bg="#263D42")
canvas.pack(side=LEFT,fill=BOTH,expand=1)
frame = tk.Frame(root,bg="green")
frame.place(relwidth=0.8,relheight=0.8,relx=0.1,rely=0.1)
frame.pack(fill=BOTH,expand=1)
my_scrollbar = ttk.Scrollbar(frame, orient=VERTICAL, command=canvas.yview)
my_scrollbar.pack(side=RIGHT, fill=Y)
# Configure The Canvas
canvas.configure(yscrollcommand=my_scrollbar.set)
canvas.bind('<Configure>', lambda e: canvas.configure(scrollregion = canvas.bbox("all")))
# Create ANOTHER Frame INSIDE the Canvas
second_frame = Frame(canvas)
# Add that New frame To a Window In The Canvas
canvas.create_window((0,0), window=second_frame, anchor="nw")
#rlls the game
openfile = tk.Button(second_frame,text="Roll a game",padx=10,pady=5,fg="white",bg="#263D42", command=arvonta)
openfile.pack()
#rolls a valorant agent
valorantA = tk.Button(second_frame,text='Roll valorant agent',padx=10,pady=5,fg="white",bg="#263D42",command=valorant)
valorantA.pack()
# stops program
stop = tk.Button(second_frame,text="Quit",padx=10,pady=5,fg="white",bg="#263D42",command=quitter)
stop.pack()
# deletes all info from screen.
deletor = tk.Button(second_frame,text="delete info",padx=10,pady=5,fg="white",bg="#263D42",command=delete)
deletor.pack()
root.mainloop()```
The following does most of what you want. I wrote it some time ago to test Scrollbars because they are wonky IMHO
from tkinter import *
from functools import partial
class ButtonsTest:
def __init__(self):
self.top = Tk()
self.top.title("Click a button to remove")
self.top.geometry("425x200+50+50")
Label(self.top, text=" Click a button to remove it ",
bg="lightyellow", font=('DejaVuSansMono', 12)
).grid(row=0, sticky="nsew")
Button(self.top, text='Exit', bg="orange", width=9,
command=self.top.quit).grid(row=1,column=0,
sticky="nsew")
self.add_scrollbar()
self.button_dic = {}
self.buttons()
self.top.mainloop()
##-------------------------------------------------------------------
def add_scrollbar(self):
self.canv = Canvas(self.top, relief=SUNKEN)
self.canv.config(width=400, height=200)
self.top_frame = Frame(self.canv, height=100)
##---------- scrollregion has to be larger than canvas size
## otherwise it just stays in the visible canvas
self.canv.config(scrollregion=(0,0, 400, 500))
self.canv.config(highlightthickness=0)
ybar = Scrollbar(self.top, width=15, troughcolor="lightblue")
ybar.config(command=self.canv.yview)
## connect the two widgets together
self.canv.config(yscrollcommand=ybar.set)
ybar.grid(row=3, column=2, sticky="ns")
self.canv.grid(row=3, column=0)
self.canv.create_window(1,0, anchor=NW,
window=self.top_frame)
##-------------------------------------------------------------------
def buttons(self):
b_row=1
b_col=0
for but_num in range(1, 51):
## create a button and send the button's number to
## self.cb_handler when the button is pressed
b = Button(self.top_frame, text = str(but_num), width=5,
command=partial(self.cb_handler, but_num))
b.grid(row=b_row, column=b_col)
## dictionary key=button number --> button instance
self.button_dic[but_num] = b
b_col += 1
if b_col > 4:
b_col = 0
b_row += 1
##----------------------------------------------------------------
def cb_handler( self, cb_number ):
print("\ncb_handler", cb_number)
self.button_dic[cb_number].grid_forget()
##===================================================================
BT=ButtonsTest()

How to delete popup window in tkinter?

I have a sequence of pop up windows. I intended to close the window once i have completed the desired task. I am using a "askokcancel" button to get users confirmation whether the activity has completed. The problem is, every time the user presses ok, the focus goes back to the main starting window and rest of the pop up windows goes to the background while staying active. I want to either close the pop up windows or keep the focus to the second last window. Below is my code:
import tkinter as tk
from tkinter import ttk, StringVar, messagebox
from tkinter.filedialog import askopenfilename
from mytest import *
from tkinter import *
class myclass:
def __init__(self, master):
self.master = master
self.frame1 = tk.Frame(self.master)
self.button1 = tk.Button(self.frame1, text = 'select me first', width = 25, command = self.buttonFunc)
self.button1.pack()
self.quitButton = tk.Button(self.frame1, text = 'Quit', width = 25, command = self.close_windows1)
self.quitButton.pack()
self.frame1.pack()
self.master.geometry("200x200+60+60")
def buttonFunc(self):
self.top = tk.Toplevel(self.master)
self.button2 = tk.Button(self.top,text="Select second",command=self.anotherButtonFunc)
self.button2.pack()
self.quitButton = tk.Button(self.top, text = 'Quit', width = 25, command = self.close_windows2)
self.quitButton.pack()
self.master.geometry("200x200+60+60")
def anotherButtonFunc(self):
self.top2 = tk.Toplevel(self.top)
self.newClass = myClassExt(self.top2)
def close_windows1(self):
self.master.destroy()
def close_windows2(self):
self.top.destroy()
class myClassExt():
def __init__(self, top2):
self.top3 = top2
self.frame2 = tk.Frame(self.top3)
self.button3 = tk.Button(self.frame2, text = 'select me third', width = 25, command = self.buttonFunc)
self.button3.pack()
self.quitButton = tk.Button(self.frame2, text = 'Quit', width = 25, command = self.close_windows4)
self.quitButton.pack()
self.frame2.pack()
self.top3.geometry("200x200+60+60")
def buttonFunc(self):
ok = messagebox.askokcancel(message='Press OK to Confirm?')
if not ok:
pass
else:
messagebox.showinfo("Success","Well done")
self.close_windows4()
def close_windows4(self):
self.top3.destroy()
if __name__ == "__main__":
root = tk.Tk()
myclass = myclass(root)
root.mainloop()
From this made up example, i somehow want to either close window number 2 after user presses OK or keep the focus on window 2 rather than window 1. Please guide
There is no way to close a message box, although you can easily make your own. You just have to make a new tkinter window, and set an image, title, and text, then add a close button, and return the tk window. I made a function like this myself, for this very reason. Here is the function:
def mymessage(title, text, spacing = 25, buttonText = "Close", image = None):
tk2 = Tk()
tk2.resizable(0, 0)
tk2.title(title)
if image != None:
image = Label(tk2, image = PhotoImage(file = image))
image.pack()
spacer = Frame(tk2, relief = FLAT, bd = 0, width = 200, height = 25)
spacer.pack()
label = Label(tk2, text = text)
label.pack()
button = Button(tk2, text = buttonText, width = 5, height = 1, command = tk2.destroy)
button.pack()
return tk2
After calling the function, it returns the tk window, for easy destruction.

Tkinter Listbox and Scrollbar not displaying

I am having this issue where the scroll bar is not displaying on the listbox. I do not know what the issue is as.
I believe the issue is originating from the Scrollbar variables as the Listbox appears to be displaying and functioning properly.
The output is displaying the listbox with no scrollbar on the right (as set)
Here is the Listbox with the for loop however, it is displaying the wrong dimensions
Here is the code:
#imports
from tkinter import *
from tkinter import messagebox as ms
from tkinter import ttk
import sqlite3
from PIL import Image,ImageTk
import datetime
global time
time = datetime.datetime.now()
class main:
def __init__(self,master):
self.master = master
def search_user_sql(self):
self.search_user_sqlf = Frame(self.master, height=300, width =200)
scrollbar = Scrollbar(self.search_user_sqlf)
scrollbar.pack(side = RIGHT,fill = BOTH)
myList = Listbox(self.search_user_sqlf, yscrollcommand= scrollbar.set)
myList.pack( side = LEFT, fill = BOTH, expand = 2)
scrollbar.config( command = myList.yview )
self.search_user_sql()
root = Tk()
root.title("Gym Membership System")
main(root)
root.mainloop()
You need to pack the frame to display it. To pack() the frame with the correct size settings, try:
search_user_sqlf = Frame(master, height=300, width=200)
search_user_sqlf.pack(expand=True, fill='both')
search_user_sqlf.pack_propagate(0)
Here is how to attach a scrollbar to list set in a frame in Tkinter:
from tkinter import *
master = Tk()
search_user_sqlf = Frame( master, width=400, height=400)
search_user_sqlf.pack(expand=True, fill='both')
search_user_sqlf.pack_propagate(0)
scrollbar = Scrollbar(search_user_sqlf)
scrollbar.pack(side=RIGHT, fill=Y)
myList = Listbox(search_user_sqlf, yscrollcommand=scrollbar.set)
for line in range(100):
myList.insert(END, "This is line number " + str(line))
myList.pack( side = LEFT, fill = BOTH , expand = 2)
scrollbar.config( command = myList.yview )
mainloop()

How to make Tkinter button to be placed in particular position?

I am new to python so I was trying to make a GUI, in that I have to place a button in a particular position.
I tried using self.nxt_form.place(x=200,y=100) instead of self.nxt_form.pack().
But the button disappeared and only the frame appeared when it ran. Can you tell me how to place the button in a particular position?
Here is the code:
import tkinter as tk
class Main_form:
def __init__(self, root,title="Simulated MTBF"):
self.root = root
self.frame = tk.Frame(self.root)
"""Button nxt_form which moves to next form"""
self.nxt_form = tk.Button(self.frame, text = 'Next Form', width = 25,command = self.new_window)
self.nxt_form.pack()
self.frame.pack()
"""command to open new window by clicking Button """
def new_window(self):
self.newWindow = tk.Toplevel(self.root)
self.app = Demo2(self.newWindow)
class Demo2:
def __init__(self, root):
self.root = root
self.frame = tk.Frame(self.root)
self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows)
self.quitButton.pack()
self.frame.pack()
def close_windows(self):
self.root.destroy()
def main():
root = tk.Tk()
app = Main_form(root)
root.mainloop()
if __name__ == '__main__':
main()
when i am using tkinter i used column and row to position objects
self.btn = tk.Button(self, text = "button")
self.btn.grid(row = 1, column = 1)
EDIT - expanded on information in response to comment (below)
I would make an label and change its width and height to make the spacing you need (note im a beginer at python as well so this is probly a bad way but it works)
from tkinter import *
import tkinter as tk
from tkinter.ttk import Combobox,Treeview,Scrollbar
class MainMenu(Frame):
def __init__(self, master):
""" Initialize the frame. """
super(MainMenu, self).__init__(master)
self.grid()
self.create_GUI()
def create_GUI(self):
frame1 = tk.LabelFrame(self, text="frame1", width=300, height=130, bd=5)
frame1.grid(row=0, column=0, columnspan=3, padx=8)
#the frame is not needed but it is a good thing to use as can group
#parts of your interface together
self.text1 = Entry(frame1)
#note if you were not using frames would just put self here
self.text1.grid(row = 1, column = 0)
self.text2 = Label(frame1, text = "",height = 10)
self.text2.grid(row = 2 , column = 0)
self.text3 = Entry(frame1)
self.text3.grid(row = 3, column = 0)
root = Tk()
root.title("hi")
root.geometry("500x500")
root.configure(bg="white")
app = MainMenu(root)
root.mainloop()
Also note that you can not use pack and grid together what you could do is group your objects in different frames then use grid in one frame and pack in a different frame. I personally prefer to use grid to pack as it gives you more control over your object then pack does

Need help placing buttons on a PhotoImage .gif

Here is my program as of yet:
from tkinter import *
from collections import deque
class App():
def __init__(self, *images):
self.root = Tk()
self.root.title("Skin")
self.image_dict = {image: PhotoImage(file=image) for image in images}
self.image_queue = deque(images)
b = Button(self.root, text="Click here to see the diagram!", command=self.change_image)
b.pack(fill=X)
self.label = Label(self.root, image=self.image_dict["1.gif"])
self.label.image = self.image_dict["1.gif"]
self.label.pack()
def change_image(self):
self.image_queue.rotate(-1)
next_image = self.image_queue[0]
self.label.configure(image=self.image_dict[next_image])
self.label.image = self.image_dict[next_image]
if __name__ == "__main__":
app = App('1.gif', '2.gif')
app.root.mainloop()
What this does is when you run the scipt, a window comes up diplaying "1.gif", and a button. When you click the button, "1.gif" changes to "2.gif". "1.gif" is a blank diagram, "2.gif" is a diagram with labels showing what each part of the diagram is.
Now for the next stage of my program, I need some way to add multiple invisible buttons, or something like it, over each word on the diagram on "2.gif", and when you click on it, I need a seperate window to come up with text on it. Is there any way to implement that into my current program? I have no idea where to start. Thank you!
I think you will be better off using a Canvas to hold your image(s) rather
than a Label. You can then place 'hotspots' over the diagram and bind
events to them. eg. something like:
from tkinter import *
class App():
def __init__(self):
self.root = Tk()
self.messages = {}
self.canvas = Canvas(self.root, bd=0, highlightthickness=0)
self.image = PhotoImage(file='2.gif')
self.canvas.create_image(0,0, image=self.image, anchor='nw')
w,h = self.image.width(), self.image.height()
self.canvas.config(width=w, height=h)
self.canvas.pack()
self.balloon = b = Toplevel(self.root)
b.withdraw()
b.wm_overrideredirect(1)
self.msg = Label(b, bg='black', fg='yellow', bd=2, text='')
self.msg.pack()
self.set_up_hotspots()
def set_up_hotspots(self):
box1 = self.canvas.create_polygon(50,100,100,100,100,150,50,150,
outline='blue', fill='')
#note: outline can be '' also ;)
self.messages[box1] = "The machine head consists of a "\
"Flynn mechanism,\na Demmel crank, "\
"and a heavy-duty frobulator. "
box2 = self.canvas.create_polygon(150,100,200,100,200,150,150,150,
outline='red', fill='')
self.messages[box2] = "And now for something completely different"
for item in [box1, box2]:
for event, handler in [('<Enter>', self.on_hotspot_enter),
('<Leave>', self.on_hotspot_leave)]:
self.canvas.tag_bind(item, event, handler)
def on_hotspot_enter(self, event):
if not self.balloon.winfo_ismapped():
txt = self.messages[event.widget.find_withtag('current')[0]]
self.msg.config(text=txt)
x,y = event.x_root, event.y_root
self.balloon.geometry('+%d+%d' %(x+16,y))
self.balloon.deiconify()
def on_hotspot_leave(self, event):
if self.balloon.winfo_ismapped():
self.balloon.withdraw()
if __name__ == "__main__":
app = App()
app.root.mainloop()

Categories