#1
  1. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Location
    California, USA
    Posts
    78
    Rep Power
    2

    Synchronize Scrolling of 2 listboxes


    I've had no luck in searching for the solution to this. How do you
    control scrolling of two listboxes from one scrollbar, so that the
    items in both stay in sync?
    Thanks.
    Code:
    from tkinter import *
    
    root = Tk()
    root.geometry('480x250+20+20')
    
    class dual_lists():
    
        def __init__(self, root):
    
            self.frame = Frame(root, borderwidth=1, relief=GROOVE)
            self.frame.pack()
            self.frame.place(x=40, y=40)
    
            self.listbox1 = Listbox(self.frame, font=('Courier', 12, 'roman'),
                width=16, height=6)
            self.listbox1.pack(side=LEFT, fill=Y)
            
            self.listbox2 = Listbox(self.frame, font=('Courier', 12, 'roman'),
                width=16, height=6)
            self.listbox2.pack(side=LEFT, fill=Y)
    
            self.yscroll = Scrollbar(self.frame, command=self.listbox1.yview, orient=VERTICAL)
            self.yscroll.pack(side=RIGHT, fill=Y)
    
            self.listbox1.configure(yscrollcommand=self.yscroll.set)
            #self.listbox2.configure(yscrollcommand=self.yscroll.set)
            
            closeBtn = Button(padx = 4, pady = 4, text = "Exit",
                font=('Helvetica', 12), command = self.close_window)
            closeBtn.pack()
            closeBtn.place(x=180, y=200)
           
            for items in [('apple', 'fruit'), ('plum', 'fruit'),('carrot', 'vegetable'),
                    ('watermellon', 'fruit'), ('bell pepper', 'vegetable'), ('lime', 'fruit'),
                    ('apricot', 'fruit'), ('potato', 'vegetable'), ('peach', 'fruit')]:
                self.listbox1.insert(END, items[0])
                self.listbox2.insert(END, items[1])
    
        def close_window(self):
            root.destroy()
    
    def main():
        
        app = dual_lists(root)
        root.mainloop()
    
    if __name__=='__main__':
        main()
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Location
    California, USA
    Posts
    78
    Rep Power
    2
    While I'm waiting for suggestions on how to synchronize scrolling
    between two listboxes, I'll post my code that, if it can't be done,
    might be a workaround.
    Do they teach this topic in any books?
    Code:
    from tkinter import *
    from collections import deque
    
    root = Tk()
    root.geometry('480x250+20+20')
    
    class dual_lists():
    
        def __init__(self, root):
    
            self.d2 = deque(maxlen=2)
            self.d2.append(1)     
                    
            self.frame = Frame(root, borderwidth=1, relief=GROOVE)
            self.frame.pack()
            self.frame.place(x=40, y=40)
    
            self.listbox1 = Listbox(self.frame, font=('Courier', 12, 'roman'),
                width=16, height=6)
            self.listbox1.pack(side=LEFT, fill=Y)
            
            self.listbox2 = Listbox(self.frame, font=('Courier', 12, 'roman'),
                width=16, height=6)
            self.listbox2.pack(side=LEFT, fill=Y)
            
            self.yscroll = Scrollbar(self.frame, command=self.listbox1.yview, orient=VERTICAL)
            self.yscroll.pack(side=RIGHT, fill=Y)
            
            self.listbox1.configure(yscrollcommand=self.scroll_box1)
                  
            closeBtn = Button(padx = 4, pady = 4, text = "Exit",
                font=('Helvetica', 12), command = self.close_window)
            closeBtn.pack()
            closeBtn.place(x=180, y=200)
            
            for items in [('apple', 'fruit'), ('plum', 'fruit'),('carrot', 'vegetable'),
                    ('watermellon', 'fruit'), ('bell pepper', 'vegetable'), ('lime', 'fruit'),
                    ('apricot', 'fruit'), ('potato', 'vegetable'), ('peach', 'fruit')]:
                self.listbox1.insert(END, items[0])
                self.listbox2.insert(END, items[1])
    
            self.listbox2.config(state=DISABLED)
     
        def scroll_box1(self, *args):
            self.yscroll.set(args[0], args[1])
            self.d2.append(float(args[1]))
            if self.d2[1] > self.d2[0]:
                yFactor = 1
            else:
                yFactor = -1
    
            self.listbox2.yview_scroll(yFactor,"units")
    
        def close_window(self):
            root.destroy()
    
    def main():
        
        app = dual_lists(root)
        root.mainloop()
    
    if __name__=='__main__':
        main()
    Last edited by pyJer; September 29th, 2013 at 01:53 AM.
  4. #3
  5. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    May 2012
    Location
    39N 104.28W
    Posts
    158
    Rep Power
    3
    Where you have:
    self.yscroll = Scrollbar(self.frame, command=self.listbox1.yview, orient=VERTICAL)
    I think you should make "command=<some function name>".
    Then that function should be like:
    Code:
       self.listbox1.yview
       self.listbox2.yview
    I can't test this right now but I know I've done it and I think I did it like this.

    Comments on this post

    • Dietrich agrees
  6. #4
  7. Contributing User
    Devshed Novice (500 - 999 posts)

    Join Date
    Feb 2005
    Posts
    620
    Rep Power
    65
    Here is an actual example ...
    Code:
    ''' Tk_listbox2_scroll1.py
    connect 2 Tkinter listboxes to one scrollbar action
    '''
    
    try:
        # Python2
        import Tkinter as tk
    except ImportError:
        # Python3
        import tkinter as tk
    
    class App(object):
        def __init__(self,master):
            scrollbar = tk.Scrollbar(master, orient='vertical')
            self.lb1 = tk.Listbox(master, yscrollcommand=scrollbar.set)
            self.lb2 = tk.Listbox(master, yscrollcommand=scrollbar.set)
            scrollbar.config(command=self.yview)
            scrollbar.pack(side='right', fill='y')
            self.lb1.pack(side='left', fill='both', expand=True)
            self.lb2.pack(side='left', fill='both', expand=True)
    
        def yview(self, *args):
            """connect the yview action together"""
            self.lb1.yview(*args)
            self.lb2.yview(*args)
    
    root = tk.Tk()
    # use width x height + x_offset + y_offset (no spaces!)
    root.geometry("320x180+130+180")
    root.title("connect 2 listboxes to one scrollbar")
    
    app = App(root)
    
    # load the list boxes for the test
    for n in range(64+26, 64, -1):
        app.lb1.insert(0, chr(n)+'ell')
        app.lb2.insert(0, chr(n)+'ell')
    
    root.mainloop()
    Real Programmers always confuse Christmas and Halloween because Oct31 == Dec25
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Location
    California, USA
    Posts
    78
    Rep Power
    2
    Thanks for your responses. They got me wondering if it is
    possible to have full control of the two list boxes so that the
    2 lists never get out of sync.
    This is for my learning and not exactly what I would use; my
    intent is to have more than one list box to display a 2nd column,
    or more, but only have the #1 list enabled for scrolling.
    The code below is presented as a working example of using
    directional keys and the mouse-wheel and scrollbar to scroll 2
    lists. Feel free to add your suggestions or corrections to my coding.

    Code:
    try:
        from Tkinter import *     ## Python 2.x
    except ImportError:
        from tkinter import *     ## Python 3.x
    
    root = Tk()
    root.geometry('435x250+20+20')
    root.title('Scroll Two Listboxes')
    
    class dual_lists():
    
        def __init__(self, root):
            
            self.frame = Frame(root, borderwidth=1, relief=GROOVE)
            self.frame.pack()
            self.frame.place(x=40, y=40)
    
            self.scrollbar = Scrollbar(self.frame, orient=VERTICAL, command=self.OnVsb) 
            self.scrollbar.pack(side=RIGHT, fill=Y)
            
            self.listbox1 = Listbox(self.frame, font=('Courier', 12, 'roman'),
                width=16, height=6)
            self.listbox1.pack(side=LEFT, fill=Y)
            
            self.listbox2 = Listbox(self.frame, font=('Courier', 12, 'roman'),
                width=16, height=6)
            self.listbox2.pack(side=LEFT, fill=Y)
              
            self.listbox1.configure(yscrollcommand=self.scrollbar.set)
            self.listbox2.configure(yscrollcommand=self.scrollbar.set)
            
            closeBtn = Button(padx = 4, pady = 4, text = "Exit",
                font=('Helvetica', 12), command = self.close_window)
            closeBtn.pack()
            closeBtn.place(x=20, y=200)
    
            txt = 'Click on a listbox and use these keys to scroll:\n' + \
                  'Home, End, PageUp, PageDown,\n' \
                  'UpArrow, DownArrow, and the Mousewheel' 
            Label(root, text=txt, font=('Helvetica', 10)).place(x=90, y=180)
            
            for n in range(64+26, 64, -1):
                self.listbox1.insert(0, chr(n)+'able')
                self.listbox2.insert(0, chr(n)+'able')
    
            self.listbox1.bind('<Up>', lambda event: self.scroll_listboxes(-1))
            self.listbox2.bind('<Up>', lambda event: self.scroll_listboxes(-1))
            self.listbox1.bind('<Down>', lambda event: self.scroll_listboxes(1))
            self.listbox2.bind('<Down>', lambda event: self.scroll_listboxes(1))
    
            self.listbox1.bind('<End>', self.end_pressed)
            self.listbox2.bind('<End>', self.end_pressed)
            self.listbox1.bind('<Home>', self.home_pressed)
            self.listbox2.bind('<Home>', self.home_pressed)
    
            self.listbox1.bind('<Next>', self.pgdown_pressed)
            self.listbox2.bind('<Next>', self.pgdown_pressed)
            self.listbox1.bind('<Prior>', self.pgup_pressed)
            self.listbox2.bind('<Prior>', self.pgup_pressed)
    
            self.listbox1.bind("<MouseWheel>", self.OnMouseWheel)
            self.listbox2.bind("<MouseWheel>", self.OnMouseWheel)
    
            self.listbox1.focus_set()  #set up listbox1 for immediate scrolling
            self.listbox1.activate(0)  #first scrolling will scroll away from listbox item #1  
    
        def OnMouseWheel(self, event):
            if event.num == 5 or event.delta == -120:
                yFactor = 1
            else:
                yFactor = -1
            self.listbox1.yview("scroll", yFactor, "units")
            self.listbox2.yview("scroll", yFactor, "units")
            return "break"
            
        def OnVsb(self, *args):    #vertical scrollbar position changed 
            self.listbox1.yview(*args)
            self.listbox2.yview(*args)
            
        def home_pressed(self, event):
            self.listbox1.see(0)
            self.listbox1.activate(0)    #added
            self.listbox2.see(0)
            self.listbox2.activate(0)    #added
            self.clear_sel()
    
        def end_pressed(self, event):
            lboxSize = self.listbox1.size()
            self.listbox1.see(lboxSize)
            self.listbox1.activate(lboxSize)    #added
            self.listbox2.see(lboxSize)
            self.listbox2.activate(lboxSize)    #added
            self.clear_sel()      
         
        def pgup_pressed(self, event):
            self.listbox1.yview_scroll(-(self.listbox1['height']), "units")
            self.listbox2.yview_scroll(-(self.listbox2['height']), "units")
            self.clear_sel()
            return "break"
            
        def pgdown_pressed(self, event):
            self.listbox1.yview_scroll(self.listbox1['height'], "units")
            self.listbox2.yview_scroll(self.listbox2['height'], "units")
            self.clear_sel()
            return "break"
       
        def scroll_listboxes(self, yFactor):
            #function runs when a listbox has focus and the Up or Down arrow keys are pressed
            self.listbox1.yview_scroll(yFactor, "units")
            self.listbox2.yview_scroll(yFactor, "units")
    
        def clear_sel(self):
            self.listbox1.selection_clear(0, END)
            self.listbox2.selection_clear(0, END)
       
        def close_window(self):
            root.destroy()
    
    def main():
        
        app = dual_lists(root)
        root.mainloop()
    
    if __name__=='__main__':
        main()
    Last edited by pyJer; October 4th, 2013 at 11:33 AM.

IMN logo majestic logo threadwatch logo seochat tools logo