TreeView, ou arborescence - Python - Programmation
Marsh Posté le 26-04-2005 à 15:24:49
hello,
c'est pas mal du tout en plus je suis sur un gros projet ca va bien m'aidé.
par contre il fonctionne tres bien quand je le lance seul mais je n'arrive pas a l'utilisé dans mon programme.
voila il me dit qu'il ne trouve pas pyimage2
j'ai cherché, et trouvé la ligne qui pose probleme mais je ne voi pas pourquoi il ne le trouve pas car :
print collapsed_icon #resulta = None
if collapsed_icon == None:
self.collapsed_icon=PhotoImage(
data='R0lGODlhDwANAKIAAAAAAMDAwICAgP//////ADAwMAAAAAAA' \
'ACH5BAEAAAEALAAAAAAPAA0AAAMyGCHM+lAMMoeAT9Jtm5NDKI4Wo' \
'FXcJphhipanq7Kvu8b1dLc5tcuom2foAQQAyKRSmQAAOw==')
else:
self.collapsed_icon=collapsed_icon
print collapsed_icon #resulta = pyimage2
je ne voi pas ce qui ne fonctionne pas
si quelqu'un a une idée merci
Marsh Posté le 22-04-2005 à 14:11:09
Salut à vous!!
si vous avez besoin d'un treeview pour votre appli Python, voici mon modeste treeview afit avec Tkinter et Tkdnd:
import Tkdnd
from Tkinter import *
#------------------------------------------------------------------------------
def report_callback_exception():
"""report exception on sys.stderr."""
import traceback
import sys
sys.stderr.write("Exception in Tree control callback\n" )
traceback.print_exc()
#------------------------------------------------------------------------------
# add_node() helper object
class Struct:
"""Helper object for add_node() method"""
def __init__(self):
pass
#------------------------------------------------------------------------------
# tree node helper class
class Node:
# initialization creates node, draws it, and binds mouseclicks
def __init__(self, parent_node, id, collapsed_icon, x, y,
parent_widget=None, expanded_icon=None, label=None,
expandable_flag=0):
# immediate parent node
self.parent_node=parent_node
# internal name used to manipulate things
self.id=id
# bitmaps to be displayed
self.expanded_icon=expanded_icon
self.collapsed_icon=collapsed_icon
# tree widget we belong to
if parent_widget:
self.widget=parent_widget
else:
self.widget=parent_node.widget
# for speed
sw=self.widget
# our list of child nodes
self.child_nodes=[]
# flag that node can be expanded
self.expandable_flag=expandable_flag
self.expanded_flag=0
# add line
if parent_node and sw.line_flag:
self.h_line=sw.create_line(x, y, x-sw.dist_x, y)
else:
self.h_line=None
self.v_line=None
# draw approprate image
self.symbol=sw.create_image(x, y, image=self.collapsed_icon)
# add label
self.label=sw.create_text(x+sw.text_offset, y, text=label, anchor='w')
# single-click to expand/collapse
sw.tag_bind(self.symbol, '<1>', self.PVT_click)
sw.tag_bind(self.label, '<1>', self.PVT_click)
# for drag'n'drop target detection
sw.tag_bind(self.symbol, '<Any-Enter>', self.PVT_enter)
sw.tag_bind(self.label, '<Any-Enter>', self.PVT_enter)
# for testing (gotta make sure nodes get properly GC'ed)
#def __del__(self):
# print self.full_id(), 'deleted'
# ----- PUBLIC METHODS -----
def set_collapsed_icon(self, icon):
self.collapsed_icon=icon
if not self.expanded_flag:
self.widget.itemconfig(self.symbol, image=icon)
def set_expanded_icon(self, icon):
self.expanded_icon=icon
if self.expanded_flag:
self.widget.itemconfig(self.symbol, image=icon)
def parent(self):
return self.parent_node
def prev_sib(self):
i=self.parent_node.child_nodes.index(self)-1
if i >= 0:
return self.parent_node.child_nodes[i]
else:
return None
def next_sib(self):
i=self.parent_node.child_nodes.index(self)+1
if i < len(self.parent_node.child_nodes):
return self.parent_node.child_nodes[i]
else:
return None
# return next lower visible node
def next_visible(self):
n=self
if n.child_nodes:
# if you can go right, do so
return n.child_nodes[0]
while n.parent_node:
# move to next sibling
i=n.parent_node.child_nodes.index(n)+1
if i < len(n.parent_node.child_nodes):
return n.parent_node.child_nodes[i]
# if no siblings, move to parent's sibling
n=n.parent_node
# we're at bottom
return self
# return next higher visible node
def prev_visible(self):
n=self
if n.parent_node:
# move to previous sibling
i=n.parent_node.child_nodes.index(n)-1
if i >= 0:
# move to last child
n=n.parent_node.child_nodes[i]
while n.child_nodes:
n=n.child_nodes[-1]
else:
# punt if there's no previous sibling
if n.parent_node:
n=n.parent_node
return n
def children(self):
return self.child_nodes
def get_label(self):
return self.widget.itemcget(self.label, 'text')
def set_label(self, label):
self.widget.itemconfig(self.label, text=label)
# returns true if expanded, false otherwise
def expanded(self):
return self.expanded_flag
# returns true if node can be expanded
def expandable(self):
return self.expandable_flag
# get name & names of all parents as a list
def full_id(self):
if self.parent_node:
return self.parent_node.full_id()+(self.id,)
else:
return (self.id,)
def expand(self):
if not self.expanded_flag:
self.PVT_set_state(1)
def collapse(self):
if self.expanded_flag:
self.PVT_set_state(0)
# delete node from tree
# ("me_too" is a hack not to be used by external code, please)
def delete(self, me_too=1):
if not self.parent_node and me_too:
# can't delete the root node
raise ValueError, "can't delete root node"
self.PVT_delete_subtree()
# move everything up so that distance to next subnode is correct
n=self.next_visible()
x1, y1=self.widget.coords(self.symbol)
x2, y2=self.widget.coords(n.symbol)
if me_too:
dist=y2-y1
else:
dist=y2-y1-self.widget.dist_y
self.PVT_tag_move(-dist)
n=self
if me_too:
if self.widget.pos == self:
# move cursor if it points to current node
self.widget.move_cursor(self.parent_node)
self.PVT_unbind_all()
self.widget.delete(self.symbol, self.label,
self.h_line, self.v_line)
self.parent_node.child_nodes.remove(self)
# break circular ref now, so parent may be GC'ed later
n=self.parent_node
self.parent_node=None
n.PVT_cleanup_lines()
n.PVT_update_scrollregion()
# call parent tree's add_node() function to generate the list of nodes
def insert_before(self, nodes):
self.parent_node.PVT_insert(nodes,
self.parent_node.child_nodes.index(self))
def insert_after(self, nodes):
self.parent_node.PVT_insert(nodes,
self.parent_node.child_nodes.index(self)+1)
def insert_children(self, nodes):
self.PVT_insert(nodes, 0)
def toggle_state(self):
if self.expandable_flag:
if self.expanded_flag:
self.PVT_set_state(0)
else:
self.PVT_set_state(1)
# ----- functions for drag'n'drop support -----
# detect mouse hover after drop
def PVT_enter(self, event):
self.widget.target=self
# poke us with a fork, we're done
# this means dnd processing has been ended, it DOES NOT imply that
# we've been dropped somewhere useful, we could have just been dropped
# into deep space and nothing happened to any data structures, or it
# could have been just a plain mouse-click w/o any dragging
def dnd_end(self, target, event):
if not self.widget.drag:
# if there's been no dragging, it was just a mouse click
self.widget.move_cursor(self)
self.toggle_state()
self.widget.drag=0
# ----- PRIVATE METHODS (prefixed with "PVT_" ) -----
# these methods are subject to change, so please try not to use them
#
# search for a particular node
def PVT_find(self, search):
self.id=os.path.normpath(self.id)
print 'search id', self.id
if os.path.normpath(self.id) != search[0]:
print 'cacca'
# this actually only goes tilt if root doesn't match
return None
if len(search) == 1:
return self
# get list of children IDs
i=map(lambda x: x.id, self.child_nodes)
# if there is a child that matches, search it
try:
return self.child_nodes[i.index(search[1])].PVT_find(search[1:])
except:
return None
# create and insert new children
# "nodes" is list previously created via calls to add_list()
# "pos" is where the new nodes are inserted in the list of children
def PVT_insert(self, nodes, pos):
if not self.expandable_flag:
raise TypeError, 'not an expandable node'
# for speed
sw=self.widget
# expand and insert children
children=[]
self.expanded_flag=1
sw.itemconfig(self.symbol, image=self.expanded_icon)
if len(nodes):
if pos == 0:
n=self
else:
n=self.child_nodes[pos-1]
# move stuff to make room
n.PVT_tag_move(sw.dist_y*len(nodes))
# get current position of icon
xp, yp=sw.coords(n.symbol)
if pos == 0:
xp=xp+sw.dist_x
# create vertical line
if sw.line_flag and not self.v_line:
self.v_line=sw.create_line(
xp, yp,
xp, yp+sw.dist_y*len(nodes))
sw.tag_lower(self.v_line, self.symbol)
n=sw.node_class
for i in nodes:
yp=yp+sw.dist_y
# add new subnodes, they'll draw themselves
# this is a very expensive call
children.append(
n(parent_node=self, expandable_flag=i.flag, label=i.name,
id=i.id, collapsed_icon=i.collapsed_icon,
expanded_icon=i.expanded_icon, x=xp, y=yp))
self.child_nodes[pos:pos]=children
self.PVT_cleanup_lines()
self.PVT_update_scrollregion()
sw.move_cursor(sw.pos)
# expanding/collapsing folders
def PVT_set_state(self, state):
# not re-entrant, and there are certain cases in which we can be
# called again before we're done
# acquire mutex
while self.widget.spinlock:
pass
self.widget.spinlock=1
# expand & draw our subtrees
if state:
self.child_nodes=[]
self.widget.new_nodes=[]
if self.widget.get_contents_callback:
# this callback needs to make multiple calls to add_node()
try:
self.widget.get_contents_callback(self)
except:
report_callback_exception()
self.PVT_insert(self.widget.new_nodes, 0)
# collapse and delete subtrees
else:
self.expanded_flag=0
self.widget.itemconfig(self.symbol, image=self.collapsed_icon)
self.delete(0)
# release mutex
self.widget.spinlock=0
# resize connecting lines
def PVT_cleanup_lines(self):
if self.widget.line_flag:
n=self
while n:
if n.child_nodes:
x1, y1=self.widget.coords(n.symbol)
x2, y2=self.widget.coords(n.child_nodes[-1].symbol)
self.widget.coords(n.v_line, x1, y1, x1, y2)
n=n.parent_node
# update scroll region for new size
def PVT_update_scrollregion(self):
x1, y1, x2, y2=self.widget.bbox('all')
self.widget.configure(scrollregion=(x1, y1, x2+5, y2+5))
# recursively delete subtree & clean up cyclic references
def PVT_delete_subtree(self):
self.widget.delete(self.v_line)
self.v_line=None
for i in self.child_nodes:
# delete node's subtree, if any
i.PVT_delete_subtree()
i.PVT_unbind_all()
# delete widgets from canvas
self.widget.delete(i.symbol, i.label, i.h_line, i.v_line)
# break circular reference
i.parent_node=None
# move cursor if it's in deleted subtree
if self.widget.pos in self.child_nodes:
self.widget.move_cursor(self)
# now subnodes will be properly garbage collected
self.child_nodes=[]
# unbind callbacks so node gets garbage-collected
# this wasn't easy to figure out the proper way to do this
def PVT_unbind_all(self):
for j in (self.symbol, self.label, self.h_line, self.v_line):
for k in self.widget.bindings.get(j, ()):
self.widget.tag_unbind(j, k[0], k[1])
# move everything below current icon, to make room for subtree
# using the Disney magic of item tags
def PVT_tag_move(self, dist):
# mark everything below current node as movable
bbox1=self.widget.bbox(self.widget.root.symbol, self.label)
bbox2=self.widget.bbox('all')
self.widget.dtag('move')
self.widget.addtag('move', 'overlapping',
bbox2[0], bbox1[3], bbox2[2], bbox2[3])
# untag cursor & node so they don't get moved too
self.widget.dtag(self.widget.cursor_box, 'move')
self.widget.dtag(self.symbol, 'move')
self.widget.dtag(self.label, 'move')
# now do the move of all the tagged objects
self.widget.move('move', 0, dist)
# handle mouse clicks by kicking off possible drag'n'drop processing
def PVT_click(self, event):
if self.widget.drop_callback:
if Tkdnd.dnd_start(self, event):
x1, y1, x2, y2=self.widget.bbox(self.symbol)
self.x_off=(x1-x2)/2
self.y_off=(y1-y2)/2
else:
# no callback, don't bother with drag'n'drop
self.widget.drag=0
self.dnd_end(None, None)
#------------------------------------------------------------------------------
class Tree(Canvas):
def __init__(self, master, root_id, root_label='',
get_contents_callback=None, dist_x=15, dist_y=15,
text_offset=10, line_flag=1, expanded_icon=None,
collapsed_icon=None, regular_icon=None, node_class=Node,
drop_callback=None, *args, **kw_args):
# pass args to superclass
# this is the ONLY proper way to handle variable arguments and
# variable keyword arguments
apply(Canvas.__init__, (self, master)+args, kw_args)
# this allows to subclass Node and pass our class in
self.node_class=node_class
# keep track of node bindings
self.bindings={}
# cheap mutex spinlock
self.spinlock=0
# flag to see if there's been any d&d dragging
self.drag=0
# default images (BASE64-encoded GIF files)
if expanded_icon == None:
self.expanded_icon=PhotoImage(
data='R0lGODlhEAANAKIAAAAAAMDAwICAgP//////ADAwMAAAAAAA' \
'ACH5BAEAAAEALAAAAAAQAA0AAAM6GCrM+jCIQamIbw6ybXNSx3GVB' \
'YRiygnA534Eq5UlO8jUqLYsquuy0+SXap1CxBHr+HoBjoGndDpNAAA7')
else:
self.expanded_icon=expanded_icon
if collapsed_icon == None:
self.collapsed_icon=PhotoImage(
data='R0lGODlhDwANAKIAAAAAAMDAwICAgP//////ADAwMAAAAAAA' \
'ACH5BAEAAAEALAAAAAAPAA0AAAMyGCHM+lAMMoeAT9Jtm5NDKI4Wo' \
'FXcJphhipanq7Kvu8b1dLc5tcuom2foAQQAyKRSmQAAOw==')
else:
self.collapsed_icon=collapsed_icon
if regular_icon == None:
self.regular_icon=PhotoImage(
data='R0lGODlhCwAOAJEAAAAAAICAgP///8DAwCH5BAEAAAMALAAA' \
'AAALAA4AAAIphA+jA+JuVgtUtMQePJlWCgSN9oSTV5lkKQpo2q5W+' \
'wbzuJrIHgw1WgAAOw==')
else:
self.regular_icon=regular_icon
# horizontal distance that subtrees are indented
self.dist_x=dist_x
# vertical distance between rows
self.dist_y=dist_y
# how far to offset text label
self.text_offset=text_offset
# flag controlling connecting line display
self.line_flag=line_flag
# called just before subtree expand/collapse
self.get_contents_callback=get_contents_callback
# called after drag'n'drop
self.drop_callback=drop_callback
# create root node to get the ball rolling
self.root=node_class(parent_node=None, label=root_label,
id=root_id, expandable_flag=1,
collapsed_icon=self.collapsed_icon,
expanded_icon=self.expanded_icon,
x=dist_x, y=dist_y, parent_widget=self)
# configure for scrollbar(s)
x1, y1, x2, y2=self.bbox('all')
self.configure(scrollregion=(x1, y1, x2+5, y2+5))
# add a cursor
self.cursor_box=self.create_rectangle(0, 0, 0, 0)
self.move_cursor(self.root)
# make it easy to point to control
self.bind('<Enter>', self.PVT_mousefocus)
# totally arbitrary yet hopefully intuitive default keybindings
# stole 'em from ones used by microsoft tree control
# page-up/page-down
self.bind('<Next>', self.pagedown)
self.bind('<Prior>', self.pageup)
# arrow-up/arrow-down
self.bind('<Down>', self.next)
self.bind('<Up>', self.prev)
# arrow-left/arrow-right
self.bind('<Left>', self.ascend)
# (hold this down and you expand the entire tree)
self.bind('<Right>', self.descend)
# home/end
self.bind('<Home>', self.first)
self.bind('<End>', self.last)
# space bar
self.bind('<Key-space>', self.toggle)
# ----- PRIVATE METHODS (prefixed with "PVT_" ) -----
# these methods are subject to change, so please try not to use them
#
# soak up event argument when moused-over
def PVT_mousefocus(self, event):
pass
# self.focus_set()
# ----- PUBLIC METHODS -----
# keep track of callback bindings so we can delete them later
# I shouldn't have to do this!!!!
def tag_bind(self, tag, seq, *args, **kw_args):
# pass args to superclass
func_id=apply(Canvas.tag_bind, (self, tag, seq)+args, kw_args)
# save references
self.bindings[tag]=self.bindings.get(tag, [])+[(seq, func_id)]
# add contents for a node to list
def add_list(self, list=None, name=None, id=None, flag=0,
expanded_icon=None, collapsed_icon=None):
n=Struct()
n.name=name
n.id=id
n.flag=flag
if collapsed_icon:
n.collapsed_icon=collapsed_icon
else:
if flag:
# it's expandable, use closed folder icon
n.collapsed_icon=self.collapsed_icon
else:
# it's not expandable, use regular file icon
n.collapsed_icon=self.regular_icon
if flag:
if expanded_icon:
n.expanded_icon=expanded_icon
else:
n.expanded_icon=self.expanded_icon
else:
# not expandable, don't need an icon
n.expanded_icon=None
if list == None:
list=[]
list.append(n)
return list
# add a node during get_contents_callback()
def add_node(self, name=None, id=None, flag=0, expanded_icon=None,
collapsed_icon=None):
self.add_list(self.new_nodes, name, id, flag, expanded_icon,
collapsed_icon)
# search for a node
def find_full_id(self, search):
return self.root.PVT_find(search)
# return node under cursor
def cursor_node(self, search):
return self.pos
# scroll (in a series of nudges) so items are visible
def see(self, *items):
x1, y1, x2, y2=apply(self.bbox, items)
while x2 > self.canvasx(0)+self.winfo_width():
old=self.canvasx(0)
self.xview('scroll', 1, 'units')
# avoid endless loop if we can't scroll
if old == self.canvasx(0):
break
while y2 > self.canvasy(0)+self.winfo_height():
old=self.canvasy(0)
self.yview('scroll', 1, 'units')
if old == self.canvasy(0):
break
# done in this order to ensure upper-left of object is visible
while x1 < self.canvasx(0):
old=self.canvasx(0)
self.xview('scroll', -1, 'units')
if old == self.canvasx(0):
break
while y1 < self.canvasy(0):
old=self.canvasy(0)
self.yview('scroll', -1, 'units')
if old == self.canvasy(0):
break
# move cursor to node
def move_cursor(self, node):
self.pos=node
x1, y1, x2, y2=self.bbox(node.symbol, node.label)
self.coords(self.cursor_box, x1-1, y1-1, x2+1, y2+1)
self.see(node.symbol, node.label)
# expand/close subtree
def toggle(self, event=None):
self.pos.toggle_state()
# move to next lower visible node
def next(self, event=None):
self.move_cursor(self.pos.next_visible())
# move to next higher visible node
def prev(self, event=None):
self.move_cursor(self.pos.prev_visible())
# move to immediate parent
def ascend(self, event=None):
if self.pos.parent_node:
# move to parent
self.move_cursor(self.pos.parent_node)
# move right, expanding as we go
def descend(self, event=None):
if self.pos.expandable_flag:
self.pos.expand()
if self.pos.child_nodes:
# move to first subnode
self.move_cursor(self.pos.child_nodes[0])
return
# if no subnodes, move to next sibling
self.next()
# go to root
def first(self, event=None):
# move to root node
self.move_cursor(self.root)
# go to last visible node
def last(self, event=None):
# move to bottom-most node
n=self.root
while n.child_nodes:
n=n.child_nodes[-1]
self.move_cursor(n)
# previous page
def pageup(self, event=None):
n=self.pos
j=self.winfo_height()/self.dist_y
for i in range(j-3):
n=n.prev_visible()
self.yview('scroll', -1, 'pages')
self.move_cursor(n)
# next page
def pagedown(self, event=None):
n=self.pos
j=self.winfo_height()/self.dist_y
for i in range(j-3):
n=n.next_visible()
self.yview('scroll', 1, 'pages')
self.move_cursor(n)
# ----- functions for drag'n'drop support -----
# determine drag location in canvas coordinates
# event.x & event.y don't seem to be what we want
def where(self, event):
# where the corner of the canvas is relative to the screen:
x_org=self.winfo_rootx()
y_org=self.winfo_rooty()
# where the pointer is relative to the canvas widget,
# including scrolling
x=self.canvasx(event.x_root-x_org)
y=self.canvasy(event.y_root-y_org)
return x, y
# accept dnd messages
# i.e. we're a legit drop target, and we do implement d&d functions
def dnd_accept(self, source, event):
self.target=None
return self
# get ready to drag or drag has entered widget (create drag object)
def dnd_enter(self, source, event):
# this flag lets us know there's been drag motion
self.drag=1
x, y=self.where(event)
x1, y1, x2, y2=source.widget.bbox(source.symbol, source.label)
dx, dy=x2-x1, y2-y1
# create dragging icon
if source.expanded_flag:
self.dnd_symbol=self.create_image(x, y,
image=source.expanded_icon)
else:
self.dnd_symbol=self.create_image(x, y,
image=source.collapsed_icon)
self.dnd_label=self.create_text(x+self.text_offset, y,
text=source.get_label(),
justify='left',
anchor='w')
# move drag icon
def dnd_motion(self, source, event):
# gets called A LOT, so no carriage return
self.drag=1
x, y=self.where(event)
x1, y1, x2, y2=self.bbox(self.dnd_symbol, self.dnd_label)
self.move(self.dnd_symbol, x-x1+source.x_off, y-y1+source.y_off)
self.move(self.dnd_label, x-x1+source.x_off, y-y1+source.y_off)
# finish dragging or drag has left widget (destroy drag object)
def dnd_leave(self, source, event):
self.delete(self.dnd_symbol)
self.delete(self.dnd_label)
# object has been dropped here
def dnd_commit(self, source, event):
# call our own dnd_leave() to clean up
self.dnd_leave(source, event)
# process pending events to detect target node
# update_idletasks() doesn't do the trick if source & target are
# on different widgets
self.update()
if not self.target:
# no target node
return
# we must update data structures based on the drop
if self.drop_callback:
try:
# called with dragged node and target node
# this is where a file manager would move the actual file
# it must also move the nodes around as it wishes
self.drop_callback(source, self.target)
except:
report_callback_exception()
#------------------------------------------------------------------------------
# the good 'ol test/demo code
if __name__ == '__main__':
import os
import sys
# default routine to get contents of subtree
# supply this for a different type of app
# argument is the node object being expanded
# should call add_node()
def get_contents(node):
path=apply(os.path.join, node.full_id())
for filename in os.listdir(path):
full=os.path.join(path, filename)
name=filename
folder=0
if os.path.isdir(full):
# it's a directory
folder=1
elif not os.path.isfile(full):
# but it's not a file
name=name+' (special)'
if os.path.islink(full):
# it's a link
name=name+' (link to '+os.readlink(full)+')'
node.widget.add_node(name=name, id=filename, flag=folder)
def quit(t):
#print t.pos.full_id()
#print t.find_full_id(('C:\\', 'AlgViewer-0.1', 'AlgViewer_doc', 'pics'))
#print t .root.PVT_find(('C:\\', 'calender', 'tcl', 'tcl8.3'))
path=apply(os.path.join,t.pos.full_id())
if os.path.isdir(path):
print path
return path
root=Tk()
root.title(os.path.basename(sys.argv[0]))
tree=os.sep
if sys.platform == 'win32':
# we could call the root "My Computer" and mess with get_contents()
# to return "A:", "B:", "C:", ... etc. as it's children, but that
# would just be terminally cute and I'd have to shoot myself
tree='C:'+os.sep
# create the control
t=Tree(master=root,
root_id=tree,
root_label=tree,
get_contents_callback=get_contents,
width=300,
bg='white')
t.grid(row=0, column=0, sticky='nsew')
# make expandable
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
# add scrollbars
sb=Scrollbar(root)
sb.grid(row=0, column=1, sticky='ns')
t.configure(yscrollcommand=sb.set)
sb.configure(command=t.yview)
sb=Scrollbar(root, orient=HORIZONTAL)
sb.grid(row=1, column=0, sticky='ew')
t.configure(xscrollcommand=sb.set)
sb.configure(command=t.xview)
# must get focus so keys work for demo
t.focus_set()
# we could do without this, but it's nice and friendly to have
# Button(root, text='Submit', command=lambda x=t:quit(x)).grid(row=2, column=0,columnspan=2)
# expand out the root
t.root.expand()
root.mainloop()
Voilà, j'espère que cela pourra vous aider!!!
Frankystadore
PS : pas besoin de chercher des infos sur les forums anglais car rares sont les informations utiles