Hi,

this is, let's say, the second part of this thread: http://www.daniweb.com/forums/thread291657.html

I have managed to control two widgets with one scrollbar, but now I have realized that, althought the widgets can be controlled individually via the mouse wheel or the keyboard, I sometimes need individual controls for each widget.


This is tony's code from prevois thread:

try:
    from Tkinter import *
except ImportError: ## Python 3
    from tkinter import *

root = Tk()

class App:
    def __init__(self,master):
        scrollbar = Scrollbar(master, orient=VERTICAL)
        self.b1 = Text(master, yscrollcommand=scrollbar.set)
        self.b2 = Text(master, yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.b1.pack(side=LEFT, fill=BOTH, expand=1)
        self.b2.pack(side=LEFT, fill=BOTH, expand=1)
    
    def yview(self, *args):
        self.b1.yview(*args)
        self.b2.yview(*args)


app = App(root)

for item in range(0,40):
    for i in range(item):
        it=str(i)+' '
        app.b1.insert(1.0,it)
        app.b2.insert(1.0,it)
    app.b1.insert(1.0,'\n')
    app.b2.insert(1.0,'\n')
    
    
root.mainloop()

This is my try:

import tkinter.scrolledtext

try:
    from Tkinter import *
except ImportError: ## Python 3
    from tkinter import *

root = Tk()

class App:
    def __init__(self,master):
        scrollbar = Scrollbar(master, orient=VERTICAL)
        self.b1 = tkinter.scrolledtext.ScrolledText(master, yscrollcommand=scrollbar.set)
        self.b2 = tkinter.scrolledtext.ScrolledText(master, yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.b1.pack(side=LEFT, fill=BOTH, expand=1)
        self.b2.pack(side=LEFT, fill=BOTH, expand=1)
    
    def yview(self, *args):
        self.b1.yview(*args)
        self.b2.yview(*args)


app = App(root)

for item in range(0,40):
    for i in range(item):
        it=str(i)+' '
        app.b1.insert(1.0,it)
        app.b2.insert(1.0,it)
    app.b1.insert(1.0,'\n')
    app.b2.insert(1.0,'\n')
    
    
root.mainloop()

I changed the widgets to scrolled text idgets, but it causes a weird behavior in the scrollbar that affects both widgets.


Now, is there a way to scroll these widgets (either text or scrolledtext) without using a toolbar, for example, with buttons for up and down, or with the keyboard (pressing a key for scrolling up and another for scrolling down)?

Remember I'm using tkinter and python 3.x

Thank you.

Recommended Answers

All 3 Replies

Sorry, I know this code is for Python 2.x, but hopefully it can point you in the right direction. There is a piece of code that I did NOT write myself but I implemented it in one of my programs. It basically creates a few text widgets in a loop and puts them in a list, also binding the scroll bar to a custom "scrolling" function. I don't think it would be too difficult to adapt this code and rather than binding the scroll bar itself, just bind arrow keys or buttons to the custom "scrolling" function. Good luck. I've pasted the code below; an example of how to implement it is given at the bottom.

from Tkinter import *

class CustomListbox(Frame):
    def __init__(self, master, lists):
	Frame.__init__(self, master)
	self.lists = []
	for l,w in lists:
	    frame = Frame(self); frame.pack(side=LEFT, expand=YES, fill=BOTH)
	    Button(frame, text=l, bg="#000000", fg="green", activeforeground="#999999", activebackground="#000000", relief=FLAT, highlightthickness=1, highlightbackground="#414141", highlightcolor="#414141", font=("Calibri", 9)).pack(fill=X)
	    lb = Listbox(frame, bg="#000000", fg="green", highlightthickness=1, width=w, borderwidth=0, selectborderwidth=0,
			 relief=FLAT, exportselection=FALSE)
	    lb.pack(expand=YES, fill=BOTH)
	    self.lists.append(lb)
	    lb.bind('<B1-Motion>', lambda e, s=self: s._select(e.y))
	    lb.bind('<Button-1>', lambda e, s=self: s._select(e.y))
	    lb.bind('<Leave>', lambda e: 'break')
	    lb.bind('<B2-Motion>', lambda e, s=self: s._b2motion(e.x, e.y))
	    lb.bind('<Button-2>', lambda e, s=self: s._button2(e.x, e.y))
	frame = Frame(self); frame.pack(side=LEFT, fill=Y)
	Label(frame, borderwidth=1, relief=RAISED).pack(fill=X)
	sb = Scrollbar(frame, orient=VERTICAL, command=self._scroll, bg="black", activebackground="#313131")
	sb.pack(expand=YES, fill=Y)
	self.lists[0]['yscrollcommand']=sb.set

    def _select(self, y):
	row = self.lists[0].nearest(y)
	self.selection_clear(0, END)
	self.selection_set(row)
	return 'break'

    def _button2(self, x, y):
	for l in self.lists: l.scan_mark(x, y)
	return 'break'

    def _b2motion(self, x, y):
	for l in self.lists: l.scan_dragto(x, y)
	return 'break'

    def _scroll(self, *args):
	for l in self.lists:
	    apply(l.yview, args)

    def curselection(self):
	return self.lists[0].curselection()

    def delete(self, first, last=None):
	for l in self.lists:
	    l.delete(first, last)

    def get(self, first, last=None):
	result = []
	for l in self.lists:
	    result.append(l.get(first,last))
	if last: return apply(map, [None] + result)
	return result
	    
    def index(self, index):
	self.lists[0].index(index)

    def insert(self, index, *elements):
	for e in elements:
	    i = 0
	    for l in self.lists:
		l.insert(index, e[i])
		i = i + 1

    def size(self):
	return self.lists[0].size()

    def see(self, index):
	for l in self.lists:
	    l.see(index)

    def selection_anchor(self, index):
	for l in self.lists:
	    l.selection_anchor(index)

    def selection_clear(self, first, last=None):
	for l in self.lists:
	    l.selection_clear(first, last)

    def selection_includes(self, index):
	return self.lists[0].selection_includes(index)

    def selection_set(self, first, last=None):
	for l in self.lists:
	    l.selection_set(first, last)

if __name__ == '__main__':
    root = Tk()
    Label(root, text='MultiListbox').pack()
    mlb = MultiListbox(root, (('Subject', 40), ('Sender', 20), ('Date', 10)))
    for i in range(1000):
	mlb.insert(END, ('Important Message: %d' % i, 'John Doe', '10/10/%04d' % (1900+i)))
    mlb.pack(expand=YES,fill=BOTH)
    root.mainloop()

By 2to3 to remove the apply, fixing the inconsistent space/tabs and Changing the main to create CustomListBox:

try:
    from tkinter import *
except ImportError:
    from Tkinter import *

class CustomListbox(Frame):
    def __init__(self, master, lists):
        Frame.__init__(self, master)
        self.lists = []
        for l,w in lists:
            frame = Frame(self); frame.pack(side=LEFT, expand=YES, fill=BOTH)
            Button(frame, text=l, bg="#000000", fg="green", activeforeground="#999999", activebackground="#000000", relief=FLAT, highlightthickness=1, highlightbackground="#414141", highlightcolor="#414141", font=("Calibri", 9)).pack(fill=X)
            lb = Listbox(frame, bg="#000000", fg="green", highlightthickness=1, width=w, borderwidth=0, selectborderwidth=0,
                 relief=FLAT, exportselection=FALSE)
            lb.pack(expand=YES, fill=BOTH)
            self.lists.append(lb)
            lb.bind('<B1-Motion>', lambda e, s=self: s._select(e.y))
            lb.bind('<Button-1>', lambda e, s=self: s._select(e.y))
            lb.bind('<Leave>', lambda e: 'break')
            lb.bind('<B2-Motion>', lambda e, s=self: s._b2motion(e.x, e.y))
            lb.bind('<Button-2>', lambda e, s=self: s._button2(e.x, e.y))
        frame = Frame(self); frame.pack(side=LEFT, fill=Y)
        Label(frame, borderwidth=1, relief=RAISED).pack(fill=X)
        sb = Scrollbar(frame, orient=VERTICAL, command=self._scroll, bg="black", activebackground="#313131")
        sb.pack(expand=YES, fill=Y)
        self.lists[0]['yscrollcommand']=sb.set

    def _select(self, y):
        row = self.lists[0].nearest(y)
        self.selection_clear(0, END)
        self.selection_set(row)
        return 'break'

    def _button2(self, x, y):
        for l in self.lists: l.scan_mark(x, y)
        return 'break'

    def _b2motion(self, x, y):
        for l in self.lists: l.scan_dragto(x, y)
        return 'break'

    def _scroll(self, *args):
        for l in self.lists:
            l.yview(*args)

    def curselection(self):
        return self.lists[0].curselection()

    def delete(self, first, last=None):
        for l in self.lists:
            l.delete(first, last)

    def get(self, first, last=None):
        result = []
        for l in self.lists:
            result.append(l.get(first,last))
        if last: return list(map(*[None] + result))
        return result
        
    def index(self, index):
        self.lists[0].index(index)

    def insert(self, index, *elements):
        for e in elements:
            i = 0
            for l in self.lists:
                l.insert(index, e[i])
                i = i + 1

    def size(self):
        return self.lists[0].size()

    def see(self, index):
        for l in self.lists:
            l.see(index)

    def selection_anchor(self, index):
        for l in self.lists:
            l.selection_anchor(index)

    def selection_clear(self, first, last=None):
        for l in self.lists:
            l.selection_clear(first, last)

    def selection_includes(self, index):
        return self.lists[0].selection_includes(index)

    def selection_set(self, first, last=None):
        for l in self.lists:
            l.selection_set(first, last)

if __name__ == '__main__':
    root = Tk()
    Label(root, text='CustomListbox').pack()
    mlb = CustomListbox(root, (('Subject', 40), ('Sender', 20), ('Date', 10)))
    for i in range(1000):
        mlb.insert(END, ('Important Message: %d' % i, 'John Doe', '10/10/%04d' % (1900+i)))
        mlb.pack(expand=YES,fill=BOTH)
    root.mainloop()

Thank you very much everybody (forgot to thank and mark as solved, sory)

commented: Better late than never ;) +12
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.