Tkinter Scrollbar inactive untill the window is manually resized - python-3.x

The scrollbar activates only when the window is rezised, despite reconfiguring the scrollregion everytime a widget(Button in this case) is added.
class Editor:
def __init__(self,master):
self.master = master
self.master.resizable(True,False)
self.edit_frame = tk.Frame(self.master)
self.edit_frame.pack(fill="none")
self.elem_frame = tk.Frame(self.master,width=700)
self.elem_frame.pack(fill="y", expand=1)
self.my_canvas = tk.Canvas(self.elem_frame, width = 700)
self.my_canvas.pack(side = "left", fill="both", expand=1)
my_scrollbar = ttk.Scrollbar(self.elem_frame, orient = "vertical", command =self.my_canvas.yview)
my_scrollbar.pack(side = "right",fill="y")
self.my_canvas.configure(yscrollcommand=my_scrollbar.set)
self.my_canvas.bind('<Configure>', self.movescroll )
self.second_frame = tk.Frame(self.my_canvas, bg = "black", width=700)
self.my_canvas.create_window((0,0), window=self.second_frame, anchor="nw")
self.naz = Nazioni()
paesi = [*self.naz.iso3]
print(paesi)
self.comb_paese = ttk.Combobox(self.edit_frame, value = paesi)
self.comb_paese.current(1)
self.kwordvar= tk.StringVar()
entry_keyword = tk.Entry(self.edit_frame, textvariable=self.kwordvar)
entry_keyword.bind("<FocusOut>", self.callback)
writer = tk.Text(self.edit_frame)
self.edit_frame.pack(side="left")
writer.pack()
self.comb_paese.pack()
entry_keyword.pack()
def callback(self, *args):
kword = self.kwordvar.get()
self.colonnarisultati(kword)
def colonnarisultati(self, keyword):
#TROVA IL VALORE CODICE ISO A 3CHAR IN BASE ALLA NAZIONE NEL COMBOBOX
p = self.comb_paese.get()
paese = self.naz.iso3[p]
ricerca = Ricerca(paese, keyword)
"""
for label in self.elem_frame.winfo_children():
label.destroy()
"""
self.count = 0
print("ciao")
for x in ricerca.listaricerca:
tit = str(x['titolo']).lstrip("<h4>").rstrip("</h4>")
print(tit)
self.elementotrovato(tit,self.count)
self.count +=1
self.my_canvas.configure(scrollregion=self.my_canvas.bbox("all"))
def movescroll(self, *args):
self.my_canvas.configure(scrollregion=self.my_canvas.bbox("all"))
def elementotrovato(self, testobottone, conta):
Bott_ecoifound = tk.Button(self.second_frame, text = testobottone,width = 100 ,height = 30, font = ("Helvetica", 12, "bold"), wraplength=200, justify="left")
print("CIAONE")
Bott_ecoifound.pack(fill="both", expand=1)
self.my_canvas.configure(scrollregion=self.my_canvas.bbox("all"))
The last method is the one adding button to the scrollable frame.
After the window is manually resized, no matter if back to its original size, the scrollbar activates and works fine.

Related

Two tkinter Objects act differently when bound on the same event

I am creating instances of two different objects (features_object and line_object) on a canvas. The idea is to connect several of the features by lines (). Later on when features are moved around the lines should follow.
At the moment my issue is that, when I apply the delete method on the lines I can delete them (Lines created between features by left mouse click. Line deleted by right mouse double click). However with the similar method of the feature class nothing happens! I cannot delete the features! The features seem invisible on the canvas! Is there any solution to that?
Thanks a lot!
from tkinter import Tk, Frame, Canvas, Text, END, TRUE, BOTH, CURRENT
click = 0
class line_object:
all_connections = []
def __init__(self):
self.connections_data = []
def create_line(self, master, start_elem, end_elem, tag_start, tag_end):
self.master = master
self.start_elem = start_elem
self.end_elem = end_elem
self.tag_start = tag_start
self.tag_end = tag_end
self.start_x = self.master.coords(self.start_elem)[0]
self.start_y = self.master.coords(self.start_elem)[1]
self.end_x = self.master.coords(self.end_elem)[0]
self.end_y = self.master.coords(self.end_elem)[1]
self.line_tag = (f'Connector_{self.start_elem}_{self.end_elem}', 'connection', 'drag')
self.master.delete(self.line_tag)
self.line_id = self.master.create_line(self.start_x, self.start_y, self.end_x, self.end_y, fill="red", width=6, tags=(self.line_tag, self.tag_start, self.tag_end))
self.master.tag_lower(self.line_id)
self.bind_events_lines()
def bind_events_lines(self):
self.master.bind('<Double Button-3>', self.deleteLine)
def deleteLine(self, event=None):
actual = self.master.find_withtag(CURRENT)
self.master.delete(actual)
class features_object():
def __init__(self):
self.features_data = []
def create_feature(self, master, feature_text, feature_xpos, feature_ypos, feature_color):
self.master = master
self.feature_text = feature_text
self.feature_xpos = feature_xpos
self.feature_ypos = feature_ypos
self.feature_color = feature_color
self.feature_frame = Frame(self.master, bg=self.feature_color, border=20)
self.feature_text = Text(self.feature_frame, font=("Helvetica", 12), relief='flat', width=20, height=3, selectbackground=self.feature_color, selectforeground="black", exportselection=TRUE)
self.feature_text.grid(row=0, column=0, padx=5, pady=5, sticky='w')
self.feature_text.config(wrap='word')
self.feature_text.insert(END, feature_text)
self.feature_tags = 'drag'
self.feature_id = self.master.create_window(self.feature_xpos, self.feature_ypos, window=self.feature_frame, tags=self.feature_tags)
self.master.itemconfigure(self.feature_id, tags=[self.feature_id])
self.bind_events_features()
def bind_events_features(self):
self.feature_frame.bind('<Button-1>', self.draw_line_with_bind)
self.feature_frame.bind('<Double Button-3>', self.deleteFeature)
def deleteFeature(self, event):
actual = self.master.find_withtag(CURRENT)
self.master.delete(actual)
def draw_line_with_bind(self, event):
global click, start_x, start_y, end_x, end_y, elem_start, tag_start
if click == 0:
elem_start = self.feature_id
tag_start = f'{elem_start}'
click = 1
elif click == 1:
elem_end = self.feature_id
tag_end = f'{elem_end}'
lin = line_object()
self.connection = lin.create_line(draw_canvas, elem_start, elem_end, tag_start, tag_end)
click = 0
window = Tk()
window.geometry("1000x800")
frame_start = Frame(window)
frame_start.pack(expand=TRUE, fill=BOTH)
draw_canvas = Canvas(frame_start)
draw_canvas.pack(expand=TRUE, fill=BOTH)
features_object().create_feature(draw_canvas, 'feature A', 100, 100, 'red')
features_object().create_feature(draw_canvas, 'feature B', 300, 300, 'green')
features_object().create_feature(draw_canvas, 'feature C', 500, 500, 'magenta')
def read_id(event):
currently_clicked = draw_canvas.find_withtag(CURRENT)
print(f'Currently clicked instance {currently_clicked}')
draw_canvas.bind('<Button-2>', read_id)
if __name__ == '__main__':
window.mainloop()

tkinter scrollbar with grid, it doesn't get linked

I'm studying GUI, so please understand my poor codes below.
I was trying to make a program which gets game-character's information. So if you press the 'search' button, the list would be shown below. But... it only shows about 11 names due to the window size. So i wanted to put a scrollbar for that area, but I just don't know how to link the scroll bar to control the area. I meant, the scroll bar itself has created, and it does scroll itself, but it doesn't scroll the window I wanted. I must have linked it wrong but... not sure.
Below is the minimized example code, but it's still quite long and crude. Sorry for that again.
If anyone can enlighten me, it would be really great help for this and my future works as well.
import tkinter as tk
import requests
from bs4 import BeautifulSoup
import webbrowser
import time
global var_dict, input_id, output
var_dict = {}
def enter_call_back(event=None):
output.grid(column = 0, row = 2, columnspan = 5 , sticky='w')
output.insert(tk.END,"Text_Something")
output.update()
search_chr()
def open_browse(url_list):
for url in url_list:
time.sleep(0.3)
webbrowser.open(url)
def search_inven(ch_id):
if ch_id == "ch1" or ch_id == "ch13" or ch_id == "ch15" :
num = 5
url_list = ["something.url","something2.url"]
self_count = 1
else:
num = 0
url_list = []
self_count = 0
masterset = []
masterset.append(num)
masterset.append(url_list)
masterset.append(self_count)
return masterset
def search_chr():
global var_dict, output
for things in var_dict.keys():
var_dict[things].destroy()
chr_list = ["ch1","ch2","ch3","ch4","ch5","ch6","ch7","ch8","ch9","ch9","ch10","ch11","ch12","ch13","ch14","ch15"]
output.insert(tk.END," Done! \n\n")
var_dict = {}
num = -1
for ch in chr_list:
num += 1
var_dict["output%s" %num] = tk.Entry(frame_buttons, width = 125)
result = search_inven(ch)
if result[0] == 0:
var_dict["output"+str(num)].insert(0, "Clean "+ch+"\n")
var_dict["output"+str(num)].grid(column = 0, row = num, sticky='w', padx=5, pady=5)
else:
url_list = result[1]
var_dict["o-button%s" %num] = tk.Button(frame_buttons, command=lambda url_list = url_list : open_browse(url_list))
var_dict["o-button"+str(num)].grid(column = 1, row = num, sticky='e')
var_dict["o-button"+str(num)].config(text="URL")
var_dict["output"+str(num)].insert(0, "Not Clean "+str(result[0])+" Self : "+str(result[2])+" Ch_id : "+ch+")\n")
var_dict["output"+str(num)].grid(column = 0, row = num, sticky='w', padx=5, pady=5)
vsb = tk.Scrollbar(frame_canvas, orient="vertical")
vsb.grid(row=0, column=1, sticky='ns')
vsb.config(command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
frame_canvas.config(height = 300)
canvas.config(scrollregion=canvas.bbox("all"))
root = tk.Tk()
root.geometry("760x710")
root.resizable(width=False, height=False)
root.title("Minimum v.1.2")
root.grid_rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
frame_main = tk.Frame(root, bg="gray")
frame_main.grid(sticky='news')
intro = tk.Text(frame_main, height = 17, bg="#E3D5F3")
intro.option_add("*Font", "명조 10")
intro.insert(tk.CURRENT,"Text_something")
intro.config(state='disabled')
intro.grid(row=0, column=0, pady=(5, 0), columnspan = 5, sticky='nw')
input_id = tk.Entry(frame_main, width = 35)
input_id.option_add("*Font","명조 10")
input_id.insert(0,"Ch_name")
input_id.grid(row=1, column=0, pady=(5, 0), sticky='w')
search_btn = tk.Button(frame_main)
search_btn.config(text="Search")
search_btn.option_add("*Font","명조 10")
search_btn.config(width=5,height=1)
search_btn.grid(row = 1, column = 0, pady=(5, 0), sticky='e')
output = tk.Text(frame_main, height = 10)
output.option_add("*Font","명조 10")
output.grid(row = 2, column = 0,pady=(5,0),sticky='nw')
frame_canvas = tk.Frame(frame_main, width = 565)
frame_canvas.grid(row=3, column=0, pady=(5, 0), columnspan = 3 ,sticky='nw')
frame_canvas.grid_rowconfigure(0, weight=1)
frame_canvas.grid_columnconfigure(0, weight=1)
frame_canvas.grid_propagate(False)
canvas = tk.Canvas(frame_canvas, bg="gray", height=500,scrollregion=(0,0,500,1800))
canvas.grid(row=0, column=0, sticky="news")
frame_buttons = tk.Frame(canvas, bg="gray")
frame_buttons.grid(row = 0, column = 0,sticky='e')
root.bind('<Return>',enter_call_back)
search_btn.config(command = enter_call_back)
root.mainloop()
First, using grid() to put frame_buttons into the canvas will not affect the scrollregion of the canvas. Use canvas.create_window() instead.
Second, it is better to bind <Configure> event on frame_buttons and update canvas' scrollregion inside the bind callback. Then adding widgets to frame_buttons will automatically update the scrollregion.
Also note that you have created new scrollbar and put it at same position whenever search_chr() is executed. Better create the scrollbar only once outside the function.

I need help making a tkinter program that variably opens a picture

I'm trying to make a program that has a button that will open a picture if a variable is in a certain state, and change how the button looks (or maybe show a different picture) if it's not. I have been trying to work through the bugs I've been getting.
This is honestly intermediary code so I can understand how to make what I'm actually trying to do, make a network-enabled GUI for some physical buttons.
I've tried passing blueButton in as a variable, but that didn't work.
import tkinter as tk
weather = "sunny"
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.pack()
self.create_widgets()
def create_widgets(self):
self.blueButton = tk.Button(self, fg = "blue")
self.blueButton["text"] = "I'm Blue"
self.blueButton["command"] = self.change
self.blueButton.pack(anchor="nw")
self.quit = tk.Button(self, text = "QUIT", fg = "red",
command = self.master.destroy)
self.quit.pack(side="bottom")
self.pack(fill = "both", expand = 1)
def change(self):
global weather
if weather == "sunny":
w = tk.Canvas(root, width=400, height=750)
img = tk.PhotoImage(file = "haunter.gif")
w.create_image((200, 200), image = img)
w.pack()
else:
self.blueButton["bitmap"] = "error"
root = tk.Tk()
root.geometry("400x300")
app = Application(master = root)
app.mainloop()
The canvas gets made, but the picture doesn't show up, the "quit" button just moves.
I've also gotten the error "name blueButton is not defined".
You could keep the image as an attribute of your App, put it on a canvas, then show or hide the canvas depending on the weather.
import random
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.pack()
self.create_widgets()
def create_widgets(self):
self.blueButton = tk.Button(self, fg = "blue")
self.blueButton["text"] = "I'm Blue"
self.blueButton["command"] = self.change
self.blueButton.pack()
self.quit = tk.Button(self, text = "QUIT", fg = "red",
command = self.master.destroy)
self.quit.pack()
self.pack(fill = "both", expand = 1)
self.canvas = tk.Canvas(root, width=400, height=750)
# self.sunny_img = tk.PhotoImage(file="haunter.gif")
self.sunny_img = tk.PhotoImage(file="rapporteur.gif")
self.canvas.create_image((200, 200), image=self.sunny_img)
def change(self):
weather = ['sunny', 'rainy']
current_weather = random.choice(weather)
if current_weather == 'sunny':
self.canvas.pack()
self.blueButton["bitmap"] = ''
else:
self.canvas.pack_forget()
self.blueButton["bitmap"] = "error"
root = tk.Tk()
root.geometry("400x300")
app = Application(master = root)
app.mainloop()

Tkinter background color issue at scrollbar

I am working on a tkinter script which has a vertical and horizontal scrollbar.
A portion of the window below the vertical scrollbar is not picking up the background color I'm applying.
I have tried the following color options still the small portion is not picking up.
Image:
Sample window snapshot
Full Code:
from tkinter.tix import *
from tkinter import *
import collections
root = Tk()
root.configure(background='steel blue')
# Global variables
fname = ''
# Variables for setting the height and width of widget
# Variables to set Height
actualRootHeight = 300
rootHScale = 25
rootHOffset = 100
canvasHeight = 300
root2CanvasHMargin =65
# Variables to set Width
rootWScale = 10
rootWOffset = 200
canvasWidth = 300
root2CanvasWMargin = 20
inpWidth = 0
# Lists to save configs
inpParamList = collections.OrderedDict()
paramListRef = collections.OrderedDict()
updatedParamList = collections.OrderedDict()
entryList = []
labels = []
# All widget coding is done here
class guiList(Frame):
global root
# Constructor - Use as a control structure
def __init__(self,parent):
Frame.__init__(self,parent)
self.parent = parent
self.readParams()
self.setGeometry()
self.initUI()
self.controlUI()
def onFrameConfigure(self, event, Canvas1):
# Reset the scroll region to encompass the inner frame
Canvas1.configure(scrollregion=Canvas1.bbox("all"), background='steel blue')
# All widget edition is done here
def initUI(self):
global paramListRef
titleStr = sys.argv[1]
self.parent.title(titleStr)
self.grid(row=0, column=0)
inpConfigs = inpParamList.items()
# Add a canvas and call Frame as it's child widget
# Scrollbar can be added to Canvas
Canvas1 = Canvas(self, width=canvasWidth, height=canvasHeight, borderwidth=0, bg="light steel blue")
vsb = Scrollbar(self, orient="vertical", command=Canvas1.yview, bg="light steel blue", troughcolor="steel blue", highlightcolor="light steel blue", activebackground="light steel blue", highlightbackground="light steel blue")
Canvas1.configure(yscrollcommand=vsb.set)
vsb.grid(column=2, sticky='NS')
hsb = Scrollbar(self, orient="horizontal", command=Canvas1.xview, bg="light steel blue", troughcolor="steel blue")
Canvas1.configure(xscrollcommand=hsb.set)
hsb.grid(column=0, sticky='EW')
Canvas1.grid(row=0, column=0, sticky='NWES')
# Create new Frame for input configs
Frame1 = Frame(Canvas1, width=canvasWidth, height=canvasHeight, bg="light steel blue")
Canvas1.create_window((1,1),window=Frame1, anchor="nw", tags="Frame1")
Frame1.bind("<Configure>", lambda event, arg=Canvas1: self.onFrameConfigure(event, arg))
# Loop through the input configs
i = 0
# Add label and combobox in loop
for k,v in inpConfigs:
# Label widgets
lbl1 = Label(Frame1, text=k, bg="light steel blue", font=("Helvetica", 12, "bold"), fg="steel blue")
lbl1.grid(row = i, column = 0, padx=10, pady=5, sticky='W')
labels.append(lbl1)
# Combo-box widget for configurations
tkvar = StringVar(Frame1)
tkvar.set(v[0])
entry1 = OptionMenu(Frame1, tkvar, *v)
entry1.configure(width=20, anchor=W, bg="steel blue", fg="white", font=("Helvetica", 11, "bold"))
entry1.grid(row = i, column=1, padx=10, pady=5, sticky='E')
#entry1.grid_columnconfigure(2, weight=2)
paramListRef[k] = tkvar
i += 1
# Read the updated configs after the button click
def readUpdatedParams(self):
global updatedParamList
for k,v in paramListRef.items():
updatedParamList[k] = v.get()
root.destroy()
self.writeBack()
# Seperate Frame for buttons
# Upon clicking read updted params
def controlUI(self):
Frame2 = Frame(self, bg="steel blue")
Frame2.grid(row=2, column = 0, sticky="EW")
b = Button(Frame2, text="OK", command=self.readUpdatedParams, bg="light steel blue", fg="steel blue", font=("Helvetica", 11, "bold"))
#b.grid(row=1, column=1, pady=10)
b.pack(fill="none", expand=True, pady = 10)
# Read the file and create a key, value pair for configs
# Lines in file is split with space as delimiter
# First column is the config name and rest are all possible value
def readParams(self):
global inpParamList
global inpWidth
f = open(fname)
for line in f:
val = {}
val = line.split()
key = val.pop(0)
# Get the max width of key to adjust widget width
inpWidth = len(key) if (len(key) > inpWidth) else inpWidth
inpParamList[key] = val
# Geometry ( X-width x Y-width + X-position + Y-position)
# Based on the number of elements in the config list
# the height of the widget is set (Max is 75% of screen size)
def setGeometry(self):
global actualRootHeight
global canvasWidth
global canvasHeight
listLen = len(inpParamList)
rootWinwidth = int(inpWidth *rootWScale) + rootWOffset
rootWinheight = (listLen * rootHScale ) + rootHOffset
screenWidth = self.winfo_screenwidth()
screenHeight = int(self.winfo_screenheight() * 0.75)
if rootWinheight < screenHeight :
actualRootHeight = rootWinheight
else :
actualRootHeight = screenHeight
canvasWidth = rootWinwidth - root2CanvasWMargin
canvasHeight = actualRootHeight - root2CanvasHMargin
rootWinresolution = str(rootWinwidth)+'x'+str(actualRootHeight)+'+'+'0'+'+'+'0'
root.geometry(rootWinresolution)
# Sub routine to write back the config file.
def writeBack(self):
fr = open("bt_top.param.config",'w')
for k,v in updatedParamList.items():
print(k,v)
fr.write(k)
fr.write(" ")
fr.write(v)
fr.write("\n")
fr.close()
# Main Function
# Define Window geometry and other top level stuff here
# Do not go into widget coding here
def main():
global fname
# Get File name from command line argument
fname = sys.argv[2]
app = guiList(root)
root.mainloop()
# The __name__ variable decides what to run
# Below lines make this file run stand-alone
if __name__ == '__main__':
Any suggestions are welcome. Thanks
-Vinay
The piece of black is the background of self, your guiList class. Add a line self.config(bg="steel blue") to its __init__() function (or to initUI() I suppose) to fix it.

Display of a large amount of data in a Canvas with a Scrollbar

I'm having a problem trying to display a large table in tkinter.
First, I tried to display all label at once in the canvas, but over few hundred rows, the program shut down. So I tried to create a scrollable canvas that updates everytime I scroll: I collect the position of the scrollbar and depending on the position of it, I display the 10 values corresponding.
But I can't get this code working. For now it only displays a black background with the scrollbar on the right.
Here is the code:
from tkinter import *
class Application(object):
def __init__(self, parent):
self.x = []
for i in range(1, 1000):
self.x.append(i)
self.parent = parent
self.mainFrame = Frame(self.parent)
self.mainFrame.pack()
self.canvas = Canvas(self.mainFrame, width = 200, height = 500, bg = "black")
self.canvas.grid(row = 0, column = 0)
self.scroll = Scrollbar(self.mainFrame, orient = VERTICAL, command = self.update)
self.scroll.grid(row = 0, column = 1)
self.canvas.configure(yscrollcommand = self.scroll.set)
self.tabCursor = 0
self.scrollPosition = self.scroll.get()
def update(self):
self.tabCursor = round(self.scrollPosition[0]*len(self.x))
if ((len(self.x) - self.tabCursor) < 10):
self.tabCursor = len(self.x) - 10
for i in range(0, 10): #display 10 values
label = Label(self.canvas, text = str(self.x[tabCursor + i]), width = 50)
label.grid(column = 0, row = i)
if __name__ == '__main__':
root = Tk()
app = Application(root)
root.mainloop()
EDIT :
I finally had time to implement your answer. It looks fine but I can't get the scrollbar working and i don't know why.
class TableauDeDonnees(object):
"Tableau de données -- Onglet Tableau de données"
def __init__(self, data, parent):
self.parent = parent
self.data = data
print(self.data[0], self.data[1])
print(len(self.data[0]), len(self.data[1]))
self.labels = []
self.navigationFrame = Frame(self.parent)
self.canvas = Canvas(self.parent, bg = "black", width = 200, height = 500)
self.mainFrame = Frame(self.canvas)
self.navigationFrame.pack()
print(len(data))
for row in range(50):
for column in range(len(data)):
self.labels.append(Label(self.canvas, text = str(data[column][row])))
for i in range(len(self.labels)):
self.labels[i].grid(row = i // 2, column = i % 2, sticky = NSEW)
self.boutonRetour = Button(self.navigationFrame, text = "Retour", command = lambda: self.move(-2))
self.quickNav = Entry(self.navigationFrame, width = 3)
self.quickNav.bind('<Return>', lambda x: self.move(self.quickNav.get()))
self.boutonSuivant = Button(self.navigationFrame, text = "Suivant", command = lambda: self.move(0))
temp = divmod(len(data[0]), len(self.labels) // 2)
self.pages = temp[0] + (1 if temp[1] else 0)
self.position = Label(self.navigationFrame, text='Page 1 sur ' + str(self.pages))
self.pageCourante = 1
self.boutonRetour.grid(row = 0, column = 0)
self.quickNav.grid(row = 0, column = 1)
self.boutonSuivant.grid(row = 0, column = 2)
self.position.grid(row = 0, column = 3)
self.scroll = Scrollbar(self.parent, orient = VERTICAL, command = self.canvas.yview)
self.canvas.configure(yscrollcommand = self.scroll.set)
self.scroll.pack(side = RIGHT, fill='y')
self.canvas.pack(side = LEFT, fill = 'both')
self.canvas.create_window((4,4), window = self.mainFrame, anchor = "nw", tags = "frame")
self.canvas.configure(yscrollcommand = self.scroll.set)
self.mainFrame.bind("<Configure>", self.update)
self.canvas.configure(scrollregion = self.canvas.bbox("all"))
def update(self, event):
self.canvas.configure(scrollregion = self.canvas.bbox("all"))
def move(self, direction):
if (self.pageCourante == 1 and direction == -2) or (self.pageCourante == self.pages and direction == 0):
return
if direction in (-2, 0):
self.pageCourante += direction + 1
else:
try:
temp = int(direction)
if temp not in range(1, self.pages + 1):
return
except ValueError:
return
else:
self.pageCourante = temp
for i in range(len(self.labels)):
try:
location = str(self.data[i % 2][len(self.labels)*(self.pageCourante - 1) + i])
except IndexError:
location = ''
self.labels[i].config(text = location)
self.position.config(text = 'Page ' + str(self.pageCourante) + ' sur ' + str(self.pages))
I don't understand why the scrollbar isn't working properly. Note, that my parent is a notebook.
Also, there is a problem with the number of items displayed. The number of pages is right but it seems it displays more than it should cause last pages are empty and the last values displayed seems right.
Thank you for your attention
The scrollbar doesn't work by continuously creating new widgets ad infinitum. You were also missing some key parts - unfortunately, Scrollbar isn't as straightforward as most tkinter widgets.
from tkinter import *
class Application(object):
def __init__(self, parent):
self.parent = parent
self.canvas = Canvas(self.parent, bg='black', width = 200, height = 500)
self.mainFrame = Frame(self.canvas)
self.scroll = Scrollbar(self.parent, orient = VERTICAL, command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.scroll.set)
self.scroll.pack(side='right', fill='y')
self.canvas.pack(side='left', fill='both')
self.canvas.create_window((4,4), window=self.mainFrame, anchor="nw", tags="frame")
self.canvas.configure(yscrollcommand = self.scroll.set)
self.mainFrame.bind("<Configure>", self.update)
self.x = []
for i in range(1000):
self.x.append(Label(self.mainFrame, text=str(i)))
self.x[i].grid()
def update(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
if __name__ == '__main__':
root = Tk()
app = Application(root)
root.mainloop()
If you'd like to show only a few at a time and provide a forum-like interface, you can use Buttons to navigate between pages. This example allows the user to navigate with Back and Forward buttons, as well as by entering a page number in the box and pressing Enter.
from tkinter import *
class Application(object):
def __init__(self, parent):
self.x = list(range(1000))
self.labels = []
self.parent = parent
self.navigation_frame = Frame(self.parent)
self.canvas = Canvas(self.parent, bg='black', width = 200, height = 500)
self.mainFrame = Frame(self.canvas)
self.navigation_frame.pack()
for i in range(100):
self.labels.append(Label(self.mainFrame, text=str(i)))
self.labels[i].grid()
self.back_button = Button(self.navigation_frame, text='Back', command=lambda: self.move(-2))
self.quick_nav = Entry(self.navigation_frame, width=3)
self.quick_nav.bind('<Return>', lambda x: self.move(self.quick_nav.get()))
self.forward_button = Button(self.navigation_frame, text='Forward', command=lambda: self.move(0))
temp = divmod(len(self.x), len(self.labels))
self.pages = temp[0] + (1 if temp[1] else 0)
self.you_are_here = Label(self.navigation_frame, text='Page 1 of ' + str(self.pages))
self.current_page = 1
self.back_button.grid(row=0, column=0)
self.quick_nav.grid(row=0, column=1)
self.forward_button.grid(row=0, column=2)
self.you_are_here.grid(row=0, column=3)
self.scroll = Scrollbar(self.parent, orient = VERTICAL, command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.scroll.set)
self.scroll.pack(side='right', fill='y')
self.canvas.pack(side='left', fill='both')
self.canvas.create_window((4,4), window=self.mainFrame, anchor="nw", tags="frame")
self.canvas.configure(yscrollcommand = self.scroll.set)
self.mainFrame.bind("<Configure>", self.update)
def update(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def move(self, direction):
if (self.current_page == 1 and direction == -2) or (self.current_page == self.pages and direction == 0):
return
if direction in (-2, 0):
self.current_page += direction + 1
else:
try:
temp = int(direction)
if temp not in range(1, self.pages+1):
return
except ValueError:
return
else:
self.current_page = temp
for i in range(len(self.labels)):
try:
location = str(self.x[len(self.labels)*(self.current_page - 1) + i])
except IndexError:
location = ''
self.labels[i].config(text=location)
self.you_are_here.config(text='Page ' + str(self.current_page) + ' of ' + str(self.pages))
if __name__ == '__main__':
root = Tk()
app = Application(root)
root.mainloop()

Resources