J'ai un problème de barre de défilement avec Tkinter. J'ai épluché pas mal d'exemple de code mais sans réussir à réadapter à mon besoin.
L'exemple ci-après montre l'interface simplifiée de mon projet. Le problème survient sur la seconde fenêtre, lorsque l'on accède au détail d'une commande.
La class "app_commande" correspond à la première fenêtre qui va lancer la seconde fenêtre class "detail_commande".
Chaque class créer un Frame. Pour avoir une barre de défilement j'ai du faire un canvas intermédiaire, le frame est alors ajouté au canvas via create_window. A priori on ne peut pas avoir une barre directement sur un Frame.
La barre de défilement est bien visible mais je n'ai aucune interaction, le contenu ne défile pas. Le positionnement avec anchor de la propriété create_window ne fonctionne pas correctement non plus.
Merci d'avance pour votre aide.
# coding: utf-8 #
# AUTHOR Jérémy BAUCHEREL #
# VERSION 0.0.1 #
# 20/06/2019 #
# ##################################################################################################### #
import Tkinter as tk
import tkFileDialog as fd
from Tkinter import *
import tkMessageBox
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ #
# -------------------------------------------------------------------------------- FONCTIONS COMMUNES -------------------------------------------------------------------------------------- #
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ #
def menu_actif(choix):
if choix == "Oui":
return "1"
elif choix == "Non":
return "0"
class HoverButton(tk.Button):
def __init__(self, master, **kw):
tk.Button.__init__(self,master=master,**kw)
self.defaultBackground = self["bg"]
self.defaultFontColor = self["fg"]
self.bind("<Enter>", self.on_enter)
self.bind("<Leave>", self.on_leave)
#Lorsque la souris passe sur le bouton (survol)
def on_enter(self, e):
self['bg'] = couleur.background_bouton_color_activebackground
self['fg'] = couleur.background_bouton_color_activeforeground
# Lorsque la souris ne survol plus le bouton
def on_leave(self, e):
self['bg'] = self.defaultBackground
self['fg'] = self.defaultFontColor
class couleur():
background_color = 'white'
background_color_banniere = '#00205e'
font_color_banniere = 'white'
background_color_tab_head = '#00205e'
font_color_tab_head = 'white'
background_bouton_color_activebackground = '#00205e'
background_bouton_color_activeforeground = '#FFFFFF'
font_color_bouton = '#00205e'
background_bouton_color = '#e9eefa'
disabledbackground = '#7e87a6'
disabledforeground = 'white'
background_color_entry = '#f1f3f9'
font_color_entry = '#00205e'
font_color_label = '#00205e'
background_color_menu_deroulant = '#f1f3f9'
font_color_menu_deroulant = '#00205e'
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ #
# -------------------------------------------------------------------------- Fenêtre d'administration des commandes ------------------------------------------------------------------------ #
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ #
class app_commande(tk.Frame):
choix = []
entry = []
buttons = []
def __init__(self, parent, tab, tab_size, tab_obligatoire, tab_actif):
tk.Frame.__init__(self, parent)
self.parent = parent
self.actif = "1"
self.tab = tab
self.tab_size = tab_size
self.tab_obligatoire = tab_obligatoire
self.tab_actif = tab_actif
self.numberLines = len(tab)
self.counter = 0
self.numberColumns = len(tab[0])
self.entries = {}
self.save_checkbox = IntVar()
self.create_widgets()
self.refresh()
def create_widgets(self):
app_commande.buttons = []
self.configure(background=couleur.background_color)
if self.actif == "1":
self.entries[self.counter] = HoverButton(self, text="ACTIF", font=('Arial',9), command=lambda: self.click_actif("1"), height = 1, width = 10, cursor='hand2', relief=FLAT, fg=couleur.background_bouton_color_activeforeground , bg=couleur.background_bouton_color_activebackground, activebackground=couleur.background_bouton_color_activebackground, activeforeground=couleur.background_bouton_color_activeforeground)
else:
self.entries[self.counter] = HoverButton(self, text="ACTIF", font=('Arial',9), command=lambda: self.click_actif("1"), height = 1, width = 10, cursor='hand2', relief=FLAT, fg=couleur.font_color_bouton , bg=couleur.background_bouton_color, activebackground=couleur.background_bouton_color_activebackground, activeforeground=couleur.background_bouton_color_activeforeground)
app_commande.buttons.append(self.entries[self.counter])
self.entries[self.counter].grid(sticky = E, row=0, column=self.numberColumns-1, pady=10)
self.counter += 1
if self.actif == "0":
self.entries[self.counter] = HoverButton(self, text ='ARCHIVE', font=('Arial',9), command=lambda: self.click_actif("0"), height = 1, width = 10, cursor='hand2', relief=FLAT, fg=couleur.background_bouton_color_activeforeground, bg=couleur.background_bouton_color_activebackground, activebackground=couleur.background_bouton_color_activebackground, activeforeground=couleur.background_bouton_color_activeforeground)
else:
self.entries[self.counter] = HoverButton(self, text ='ARCHIVE', font=('Arial',9), command=lambda: self.click_actif("0"), height = 1, width = 10, cursor='hand2', relief=FLAT, fg=couleur.font_color_bouton, bg=couleur.background_bouton_color, activebackground=couleur.background_bouton_color_activebackground, activeforeground=couleur.background_bouton_color_activeforeground)
app_commande.buttons.append(self.entries[self.counter])
self.entries[self.counter].grid(sticky = E, row=0, column=self.numberColumns, pady=10)
self.counter += 1
for row in xrange(self.numberLines+2):
# On commence à partir de la ligne 2 de la grille
if row >0:
colonne = []
for column in xrange(self.numberColumns+1):
# Première colonne => option bouton a cocher
if column == 0:
# Ligne entête
if row == 1:
self.entries[self.counter] = Label(self, text="", bd=0, height=2, width=self.tab_size[column], fg=couleur.font_color_label, bg=couleur.background_color)
elif row == self.numberLines+1:
self.entries[self.counter] = Label(self, text="Nouveau:", bd=1, fg=couleur.font_color_label, bg=couleur.background_color)
else:
self.entries[self.counter] = Radiobutton(self, variable=self.save_checkbox, text="", value=self.tab[row-1][0], bg=couleur.background_color, relief=FLAT)
app_commande.choix.append(self.save_checkbox)
# Dernière colonne => actif
elif column == self.numberColumns:
# Ligne entête
if row == 1:
entete = self.tab[row-1][column-1]
if self.tab_obligatoire[column-1] == 1:
entete = entete + "*"
self.entries[self.counter] = Label(self, text=entete, bd=0, height=2, width=self.tab_size[column], fg=couleur.font_color_tab_head, bg=couleur.background_color_tab_head)
# Ligne de fin
elif row == self.numberLines+1:
v = StringVar()
v.set("Oui")
self.entries[self.counter] = OptionMenu(self, v, "Oui", "Non")
self.entries[self.counter].config(highlightbackground=couleur.background_color_menu_deroulant, bd=0, padx=0, pady=0, fg=couleur.font_color_menu_deroulant, bg=couleur.background_color_menu_deroulant, relief=FLAT)
colonne.append(v)
# BDD
else:
v = StringVar()
# initial value
if self.tab[row-1][column-1] == 1:
v.set("Oui")
else:
v.set("Non")
self.entries[self.counter] = OptionMenu(self, v, "Oui", "Non")
self.entries[self.counter].config(highlightbackground=couleur.background_color_menu_deroulant, bd=0, padx=0, pady=0, fg=couleur.font_color_menu_deroulant, bg=couleur.background_color_menu_deroulant, relief=FLAT)
colonne.append(v)
else:
# Ligne entête
if row == 1:
entete = self.tab[row-1][column-1]
if self.tab_obligatoire[column-1] == 1:
entete = entete + "*"
self.entries[self.counter] = Label(self, text=entete, bd=0, height=2, width=self.tab_size[column], fg=couleur.font_color_tab_head, bg=couleur.background_color_tab_head)
# Ligne de fin
elif row == self.numberLines+1:
s = StringVar()
if self.tab_actif[column-1] == 0:
state = 'disabled'
else:
state = 'normal'
if self.tab[0][column-1] == "PGM":
listeOptions = liste_pgm()
listeOptions.append("")
s.set("")
self.entries[self.counter] = OptionMenu(self, s, *listeOptions)
self.entries[self.counter].config(highlightbackground=couleur.background_color_menu_deroulant, bd=0, padx=0, pady=0, fg=couleur.font_color_menu_deroulant, bg=couleur.background_color_menu_deroulant, relief=FLAT)
elif self.tab[0][column-1] == "CENTRE DE COUT":
listeOptions = liste_centre_de_cout()
listeOptions.append("")
s.set("")
self.entries[self.counter] = OptionMenu(self, s, *listeOptions)
self.entries[self.counter].config(highlightbackground=couleur.background_color_menu_deroulant, bd=0, padx=0, pady=0, fg=couleur.font_color_menu_deroulant, bg=couleur.background_color_menu_deroulant, relief=FLAT)
else:
self.entries[self.counter] = Entry(self, textvariable=s, width=self.tab_size[column], state=state, relief=FLAT, fg=couleur.font_color_entry, bg=couleur.background_color_entry, disabledbackground=couleur.disabledbackground, disabledforeground=couleur.disabledforeground)
colonne.append(s)
# BDD
else:
s = StringVar()
if self.tab_actif[column-1] == 0:
state = 'disabled'
else:
state = 'normal'
if self.tab[0][column-1] == "PGM":
listeOptions = liste_pgm()
s.set(listeOptions[0])
self.entries[self.counter] = OptionMenu(self, s, *listeOptions)
self.entries[self.counter].config(highlightbackground=couleur.background_color_menu_deroulant, bd=0, padx=0, pady=0, fg=couleur.font_color_menu_deroulant, bg=couleur.background_color_menu_deroulant, relief=FLAT)
elif self.tab[0][column-1] == "CENTRE DE COUT":
listeOptions = liste_centre_de_cout()
s.set(listeOptions[0])
self.entries[self.counter] = OptionMenu(self, s, *listeOptions)
self.entries[self.counter].config(highlightbackground=couleur.background_color_menu_deroulant, bd=0, padx=0, pady=0, fg=couleur.font_color_menu_deroulant, bg=couleur.background_color_menu_deroulant, relief=FLAT)
else:
self.entries[self.counter] = Entry(self, textvariable=s, width=self.tab_size[column], state = state, relief=FLAT, fg=couleur.font_color_entry, bg=couleur.background_color_entry, disabledbackground=couleur.disabledbackground, disabledforeground=couleur.disabledforeground)
colonne.append(s)
if self.tab[row-1][column-1] != None:
s.set(self.tab[row-1][column-1])
if row == self.numberLines+1 and column==0:
self.entries[self.counter].grid(row=row, column=column, padx=1, pady=1, columnspan=2)
elif row == self.numberLines+1 and column==1:
pass
else:
self.entries[self.counter].grid(row=row, column=column, padx=1, pady=1)
self.counter += 1
if row > 1:
app_commande.entry.append(colonne)
self.counter += 1
self.entries[self.counter] = HoverButton(self, text ='REFRESH', font=('Arial',9), command=self.refresh, relief=FLAT, cursor='hand2', fg=couleur.font_color_bouton, bg=couleur.background_bouton_color, activebackground=couleur.background_bouton_color_activebackground, activeforeground=couleur.background_bouton_color_activeforeground)
self.entries[self.counter].grid(sticky = E, row=row+1, column=6, pady=10, columnspan=2)
self.counter += 1
self.entries[self.counter] = HoverButton(self, text ='DETAIL', font=('Arial',9), command=self.detail_commande, relief=FLAT, cursor='hand2', fg=couleur.font_color_bouton, bg=couleur.background_bouton_color, activebackground=couleur.background_bouton_color_activebackground, activeforeground=couleur.background_bouton_color_activeforeground)
self.entries[self.counter].grid(sticky = W, row=row+2, column=0, pady=10, columnspan=3)
if self.actif == "1":
app_commande.buttons[0]["fg"] = couleur.background_bouton_color_activeforeground
app_commande.buttons[0]["bg"] = couleur.background_bouton_color_activebackground
app_commande.buttons[1]["fg"] = couleur.font_color_bouton
app_commande.buttons[1]["bg"] = couleur.background_bouton_color
elif self.actif == "0":
app_commande.buttons[1]["fg"] = couleur.background_bouton_color_activeforeground
app_commande.buttons[1]["bg"] = couleur.background_bouton_color_activebackground
app_commande.buttons[0]["fg"] = couleur.font_color_bouton
app_commande.buttons[0]["bg"] = couleur.background_bouton_color
def refresh(self):
for c in range(0, self.counter+1):
try:
#self.entries[c].grid_remove()
self.entries[c].destroy()
except:
pass
self.tab = requete_commande(self.actif)
self.numberLines = len(self.tab)
self.numberColumns = len(self.tab[0])
self.entries = {}
self.counter = 0
app_commande.entry = []
app_commande.choix = []
self.create_widgets()
def hide(self):
self.parent.withdraw()
def show(self):
self.parent.update()
self.parent.deiconify()
def detail_commande(self):
baff = False
for row in xrange(self.numberLines-1):
if str(app_commande.choix[row].get()) == str(self.tab[row+1][0]):
baff = True
# Affichage de la liste des commandes
fenetre_ = Toplevel(self.parent)
fenetre_.configure(background=couleur.background_color)
# Taille de la fenêtre souhaitée
w = 1280
h = 720
# Récupération de la largeur et hauteur de l'écran
ws = fenetre_.winfo_screenwidth() # width of the screen
hs = fenetre_.winfo_screenheight() # height of the screen
# Calcul de la position x et y de la fenêtre Tk
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
# Défini la dimention et le positionnement de la fenêtre
fenetre_.geometry('%dx%d+%d+%d' % (w, h, x, y))
# Titre de la fenêtre
fenetre_.title('Détail de la commande')
tab_size_= [15,25,70,10]
tab_obligatoire_ = [1,1,0,1]
tab_actif_ = [0,0,0,1]
frame_head_ = Frame(fenetre_)
frame_head_.configure(background=couleur.background_color_banniere)
label_ = Label(frame_head_, text="Détail de la commande", font=('Arial',15), width=w, height=1, fg=couleur.font_color_banniere, bg=couleur.background_color_banniere)
label_.pack(padx=20, pady=10)
frame_head_.pack(padx=0, pady=0, fill=BOTH)
id_commande = app_commande.choix[row].get()
tab_ = requete_detail_commande(id_commande)
# On masque la fenêtre WP lorsque l'on ouvre la fenêtre livrable
self.hide()
fenetre_.protocol("WM_DELETE_WINDOW", lambda: self.fermeture_detail_commande(fenetre_))
scrollbar = tk.Scrollbar(fenetre_, orient=tk.VERTICAL)
scrollbar.pack(side='right', fill='y')
canvas = tk.Canvas(fenetre_, yscrollcommand=scrollbar.set, width=w, height=h, bd=0, highlightthickness=0, bg=couleur.background_color)
canvas.pack(fill='both', expand=True)
# Configure le scrollbar pour la vue Y du canvas
scrollbar.config(command=canvas.yview)
# Création du frame
frame_ = detail_commande(canvas, tab_, tab_size_, tab_obligatoire_, tab_actif_, id_commande)
# Ajout du frame au canvas
canvas.create_window(0, 0, anchor=tk.NE, window=frame_)
frame_.update_idletasks()
#canvas.config(scrollregion=canvas.bbox("all"))
canvas.bind('<Configure>', canvas.config(scrollregion=canvas.bbox("all")))
fenetre_.mainloop()
if baff == False:
tkMessageBox.showerror("Détail de la commande", "Vous devez sélectionner une commande (coche) pour afficher le détail correspondant", parent=self)
def fermeture_detail_commande(self, fenetre_):
fenetre_.destroy()
self.show()
self.refresh()
def click_actif(self, actif):
self.actif = actif
self.refresh()
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ #
# ---------------------------------------------------- Fenêtre d'administration des commandes, choix des livrables de la commande => qte --------------------------------------------------- #
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ #
class detail_commande(tk.Frame):
choix = []
entry = []
def __init__(self, parent, tab, tab_size, tab_obligatoire, tab_actif, id_commande):
tk.Frame.__init__(self, parent)
self.parent = parent
self.id_commande = id_commande
self.tab = tab
self.tab_size = tab_size
self.tab_obligatoire = tab_obligatoire
self.tab_actif = tab_actif
self.numberLines = len(tab)
self.counter = 0
self.numberColumns = len(tab[0])
self.entries = {}
self.save_checkbox = IntVar()
self.create_widgets()
self.refresh()
def create_widgets(self):
self.configure(background=couleur.background_color)
for row in xrange(self.numberLines):
colonne = []
for column in xrange(self.numberColumns):
# Ligne entête
if row == 0:
entete = self.tab[row][column]
if self.tab_obligatoire[column] == 1:
entete = entete + "*"
self.entries[self.counter] = Label(self, text=entete, bd=0, height=2, width=self.tab_size[column], fg=couleur.font_color_tab_head, bg=couleur.background_color_tab_head)
# BDD
else:
s = StringVar()
if self.tab_actif[column] == 0:
state = 'disabled'
else:
state = 'normal'
self.entries[self.counter] = Entry(self, textvariable=s, bd=0, width=self.tab_size[column], state=state, relief=FLAT, fg=couleur.font_color_entry, bg=couleur.background_color_entry, disabledbackground=couleur.disabledbackground, disabledforeground=couleur.disabledforeground)
colonne.append(s)
if self.tab[row][column] != None:
s.set(self.tab[row][column])
if row == 0:
self.entries[self.counter].grid(row=row, column=column, padx=0, pady=10)
else:
if column == 0:
self.entries[self.counter].config(bd=0, bg = couleur.disabledbackground)
else:
self.entries[self.counter].config(bd=0, bg = couleur.background_color_entry)
self.entries[self.counter].grid(row=row, column=column, padx=0, pady=5)
self.counter += 1
self.grid_columnconfigure(column, weight=1)
if row > 0:
detail_commande.entry.append(colonne)
self.counter += 1
self.entries[self.counter] = HoverButton(self, text ='REFRESH', font=('Arial',9), command=self.refresh, relief=FLAT, cursor='hand2', fg=couleur.font_color_bouton, bg=couleur.background_bouton_color, activebackground=couleur.background_bouton_color_activebackground, activeforeground=couleur.background_bouton_color_activeforeground)
self.entries[self.counter].grid(sticky = E, row=row+1, column=1, padx=0, pady=10)
def refresh(self):
self.grab_set()
self.focus_set()
for c in range(0, self.counter+1):
try:
#self.entries[c].grid_remove()
self.entries[c].destroy()
except:
pass
self.tab = requete_detail_commande(self.id_commande)
self.numberLines = len(self.tab)
self.numberColumns = len(self.tab[0])
self.entries = {}
self.counter = 0
detail_commande.entry = []
detail_commande.choix = []
self.create_widgets()
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ #
# --------------------------------------------------------------------------------- Requète vers la BDD ------------------------------------------------------------------------------------ #
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ #
import random
def requete_commande(actif):
tab = []
tab.append(["ID", "NUMERO", "PGM", "DESCRIPTION", "DEMANDEUR", "DATE", "CENTRE DE COUT", "ACTIF"])
tab.append(["1", "C1", "P1", "Cmd P1", "--", "07/07/2019", "CT1", "1"])
tab.append(["2", "C2", "P2", "Cmd P2", "--", "07/07/2019", "CT1", "1"])
tab.append(["3", "C3", "P3", "Cmd P3", "--", "07/07/2019", "CT2", "1"])
return tab
def requete_detail_commande(id_commande):
tab = []
tab.append(["WP", "NOM", "DESC LIVRABLE", "QTE"])
for i in range(0,30):
tab.append(["WP "+str(i), "Z1A-IT"+str(i), "-", random.randint(0, 50)])
return tab
# Requète qui liste les programmes
def liste_pgm():
tab = ["P1", "P2", "P3", "P4", "P5"]
return tab
# Requète qui liste les centres de couts
def liste_centre_de_cout():
tab = ["CT1", "CT2"]
return tab
# Fonction qui va retourner l'id du centre de cout à partir du centre de cout choisi (numéro)
def id_centre_de_cout(centre_de_cout):
return centre_de_cout[2:2]
# ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #
# ----------------------------------------------------------------------------- Administration -------------------------------------------------------------------------------------- #
# ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #
fenetre = Tk()
fenetre.configure(background=couleur.background_color)
# Taille de la fenêtre souhaitée
w = 1280
h = 720
# Récupération de la largeur et hauteur de l'écran
ws = fenetre.winfo_screenwidth() # width of the screen
hs = fenetre.winfo_screenheight() # height of the screen
# Calcul de la position x et y de la fenêtre Tk
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
# Défini la dimention et le positionnement de la fenêtre
fenetre.geometry('%dx%d+%d+%d' % (w, h, x, y))
# Titre de la fenêtre
fenetre.title('Liste des commandes')
tab_size= [10,5,20,10,45,30,15,15,10]
tab_obligatoire = [1,1,1,0,0,1,1,1]
tab_actif = [0,1,1,1,1,1,1,1,0]
frame_head = Frame(fenetre)
frame_head.configure(background=couleur.background_color_banniere)
label = Label(frame_head, text="Liste des commandes", font=('Arial',15), width=w, height=1, fg=couleur.font_color_banniere, bg=couleur.background_color_banniere)
label.pack(padx=20, pady=10)
frame_head.pack(padx=0, pady=0, fill=BOTH)
tab_r = requete_commande("1")
frame = app_commande(fenetre, tab_r, tab_size, tab_obligatoire, tab_actif)
frame.pack(pady=10)
fenetre.mainloop()
L'exemple ci-après montre l'interface simplifiée de mon projet. Le problème survient sur la seconde fenêtre, lorsque l'on accède au détail d'une commande.
Effectivement 600 lignes, c'est simplifié!
Ne pensez vous pas que c'est un peu beaucoup pour illustrer le problème?
D'autant qu'en cherchant un peu sur Internet vous avez des codes tout faits qui montrent comment avoir une Frame scrollable dans un Canvas.
Barre de défilement Tkinter
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.