Welcome, guest | Sign In | My Account | Store | Cart
# Version: 0.11
# Author: Miguel Martinez Lopez
# Uncomment the next line to see my email
# print("Author's email: ", "61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex"))

try:
    from Tkinter import Canvas, Frame
    from ttk import Scrollbar
    
    from Tkconstants import *
except ImportError:
    from tkinter import Canvas, Frame
    from tkinter.ttk import Scrollbar
    
    from tkinter.constants import *

import platform

OS = platform.system()

def build_mouse_wheel_handler(widget, orient, factor = 1, what="units"):
    view_command = getattr(widget, orient+'view')
    
    if OS == 'Linux':
        def onMouseWheel(event):
            if event.num == 4:
                view_command("scroll",(-1)*factor, what)
            elif event.num == 5:
                view_command("scroll",factor, what) 
            
    elif OS == 'Windows':
        def onMouseWheel(event):        
            view_command("scroll",(-1)*int((event.delta/120)*factor), what) 
    
    elif OS == 'Darwin':
        def onMouseWheel(event):        
            view_command("scroll",event.delta, what)
    
    return onMouseWheel

class Mousewheel_Support(object):    

    # implemetnation of singleton pattern
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Mousewheel_Support, cls).__new__(cls, *args, **kwargs)
        return cls._instance

    def __init__(self, root, horizontal_factor = 2, vertical_factor=2):
        
        self._active_area = None
        
        if isinstance(horizontal_factor, int):
            self.horizontal_factor = horizontal_factor
        else:
            raise Exception("Vertical factor must be an integer.")

        if isinstance(vertical_factor, int):
            self.vertical_factor = vertical_factor
        else:
            raise Exception("Horizontal factor must be an integer.")

        if OS == "Linux" :
            root.bind_all('<4>', self._on_mouse_wheel,  add='+')
            root.bind_all('<5>', self._on_mouse_wheel,  add='+')
        else:
            # Windows and MacOS
            root.bind_all("<MouseWheel>", self._on_mouse_wheel,  add='+')

    def _on_mouse_wheel(self,event):
        if self._active_area:
            self._active_area.onMouseWheel(event)

    def _on_mouse_enter_scrolling_area(self, widget):
        self._active_area = widget

    def _on_mouse_leave_scrolling_area(self):
        self._active_area = None

    def add_support_to(self, widget, xscrollbar=None, yscrollbar=None, what="units", horizontal_factor=None, vertical_factor=None):
        if xscrollbar is not None and not hasattr(xscrollbar, 'onMouseWheel'):
            horizontal_factor = horizontal_factor or self.horizontal_factor

            xscrollbar.onMouseWheel = build_mouse_wheel_handler(widget,'x', self.horizontal_factor, what)
            xscrollbar.bind('<Enter>', lambda event, scrollbar=xscrollbar: self._on_mouse_enter_scrolling_area(scrollbar) )
            xscrollbar.bind('<Leave>', lambda event: self._on_mouse_leave_scrolling_area())

        if yscrollbar is not None and not hasattr(yscrollbar, 'onMouseWheel'):
            vertical_factor = vertical_factor or self.vertical_factor

            yscrollbar.onMouseWheel = build_mouse_wheel_handler(widget,'y', self.vertical_factor, what)
            yscrollbar.bind('<Enter>', lambda event, scrollbar=yscrollbar: self._on_mouse_enter_scrolling_area(scrollbar) )
            yscrollbar.bind('<Leave>', lambda event: self._on_mouse_leave_scrolling_area())

        main_scrollbar = yscrollbar if yscrollbar is not None else xscrollbar
        
        if main_scrollbar is not None:
            widget.bind('<Enter>',lambda event: self._on_mouse_enter_scrolling_area(widget))
            widget.bind('<Leave>', lambda event: self._on_mouse_leave_scrolling_area())

            widget.onMouseWheel = main_scrollbar.onMouseWheel

class Scrolling_Area(Frame):

    def __init__(self, master, mousewheel_speed = 2, scroll_horizontally=True, scroll_vertically=True, outer_background=None, inner_frame=Frame, **kw):
        Frame.__init__(self, master)
        
        if outer_background:
            self.configure(background=outer_background)

        self._canvas = Canvas(self, highlightthickness=0)
        self._canvas.grid(row=0, column=0, sticky="nsew")

        if scroll_vertically:
            self.yscrollbar = Scrollbar(self, orient=VERTICAL)
            self.yscrollbar.grid(row=0, column=1,sticky=N+S)
        
            self._canvas.configure(yscrollcommand=self.yscrollbar.set)
            self.yscrollbar['command']=self._canvas.yview
        else:
            self.yscrollbar = None

        if scroll_horizontally:
            self.xscrollbar = Scrollbar(self, orient=HORIZONTAL)
            self.xscrollbar.grid(row=1, column=0, sticky=E+W)
            
            self._canvas.configure(xscrollcommand=self.xscrollbar.set)
            self.xscrollbar['command']=self._canvas.xview
        else:
            self.xscrollbar = None

        self.rowconfigure( 0, weight=1)
        self.columnconfigure(0, weight=1)

        self.innerFrame= inner_frame(self._canvas, **kw)
        self.innerFrame.pack()
        
        self._canvas.create_window(0, 0, window=self.innerFrame, anchor='nw', tags="inner_frame")
                    
        self._canvas.bind('<Configure>', self._on_configure_canvas)

        Mousewheel_Support(self).add_support_to(self._canvas, xscrollbar=self.xscrollbar, yscrollbar=self.yscrollbar)

    def _on_configure_canvas(self, event):
        width = max(self.innerFrame.winfo_reqwidth(), event.width)
        height = max(self.innerFrame.winfo_reqheight(), event.height)
        
        self._canvas.configure(scrollregion="0 0 %s %s" % (width, height))        
        self._canvas.itemconfigure("inner_frame", width=width, height=height)

if __name__== '__main__':
    try:
        from Tkinter import Tk, Label
    except ImportError:
        from tkinter import Tk, Label
    
    root = Tk()
    
    scrolling_area = Scrolling_Area(root)
    scrolling_area.pack(expand=1, fill=BOTH)
    
    for i in range(20):
        rowFrame = Frame(scrolling_area.innerFrame)
        rowFrame.pack()
        for j in range(8):
            Label(rowFrame, text="Label %s, %s" % (str(i), str(j))).pack(side="left")

    root.mainloop()

Diff to Previous Revision

--- revision 12 2017-04-07 19:57:42
+++ revision 13 2017-04-17 20:16:54
@@ -1,4 +1,4 @@
-# Version: 0.10
+# Version: 0.11
 # Author: Miguel Martinez Lopez
 # Uncomment the next line to see my email
 # print("Author's email: ", "61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex"))
@@ -7,45 +7,128 @@
     from Tkinter import Canvas, Frame
     from ttk import Scrollbar
     
-    from Tkconstants import HORIZONTAL, VERTICAL, BOTH, N, S, E, W
+    from Tkconstants import *
 except ImportError:
     from tkinter import Canvas, Frame
     from tkinter.ttk import Scrollbar
     
-    from tkinter.constants import HORIZONTAL, VERTICAL, BOTH, N, S, E, W
+    from tkinter.constants import *
 
 import platform
 
+OS = platform.system()
+
+def build_mouse_wheel_handler(widget, orient, factor = 1, what="units"):
+    view_command = getattr(widget, orient+'view')
+    
+    if OS == 'Linux':
+        def onMouseWheel(event):
+            if event.num == 4:
+                view_command("scroll",(-1)*factor, what)
+            elif event.num == 5:
+                view_command("scroll",factor, what) 
+            
+    elif OS == 'Windows':
+        def onMouseWheel(event):        
+            view_command("scroll",(-1)*int((event.delta/120)*factor), what) 
+    
+    elif OS == 'Darwin':
+        def onMouseWheel(event):        
+            view_command("scroll",event.delta, what)
+    
+    return onMouseWheel
+
+class Mousewheel_Support(object):    
+
+    # implemetnation of singleton pattern
+    _instance = None
+    def __new__(cls, *args, **kwargs):
+        if not cls._instance:
+            cls._instance = super(Mousewheel_Support, cls).__new__(cls, *args, **kwargs)
+        return cls._instance
+
+    def __init__(self, root, horizontal_factor = 2, vertical_factor=2):
+        
+        self._active_area = None
+        
+        if isinstance(horizontal_factor, int):
+            self.horizontal_factor = horizontal_factor
+        else:
+            raise Exception("Vertical factor must be an integer.")
+
+        if isinstance(vertical_factor, int):
+            self.vertical_factor = vertical_factor
+        else:
+            raise Exception("Horizontal factor must be an integer.")
+
+        if OS == "Linux" :
+            root.bind_all('<4>', self._on_mouse_wheel,  add='+')
+            root.bind_all('<5>', self._on_mouse_wheel,  add='+')
+        else:
+            # Windows and MacOS
+            root.bind_all("<MouseWheel>", self._on_mouse_wheel,  add='+')
+
+    def _on_mouse_wheel(self,event):
+        if self._active_area:
+            self._active_area.onMouseWheel(event)
+
+    def _on_mouse_enter_scrolling_area(self, widget):
+        self._active_area = widget
+
+    def _on_mouse_leave_scrolling_area(self):
+        self._active_area = None
+
+    def add_support_to(self, widget, xscrollbar=None, yscrollbar=None, what="units", horizontal_factor=None, vertical_factor=None):
+        if xscrollbar is not None and not hasattr(xscrollbar, 'onMouseWheel'):
+            horizontal_factor = horizontal_factor or self.horizontal_factor
+
+            xscrollbar.onMouseWheel = build_mouse_wheel_handler(widget,'x', self.horizontal_factor, what)
+            xscrollbar.bind('<Enter>', lambda event, scrollbar=xscrollbar: self._on_mouse_enter_scrolling_area(scrollbar) )
+            xscrollbar.bind('<Leave>', lambda event: self._on_mouse_leave_scrolling_area())
+
+        if yscrollbar is not None and not hasattr(yscrollbar, 'onMouseWheel'):
+            vertical_factor = vertical_factor or self.vertical_factor
+
+            yscrollbar.onMouseWheel = build_mouse_wheel_handler(widget,'y', self.vertical_factor, what)
+            yscrollbar.bind('<Enter>', lambda event, scrollbar=yscrollbar: self._on_mouse_enter_scrolling_area(scrollbar) )
+            yscrollbar.bind('<Leave>', lambda event: self._on_mouse_leave_scrolling_area())
+
+        main_scrollbar = yscrollbar if yscrollbar is not None else xscrollbar
+        
+        if main_scrollbar is not None:
+            widget.bind('<Enter>',lambda event: self._on_mouse_enter_scrolling_area(widget))
+            widget.bind('<Leave>', lambda event: self._on_mouse_leave_scrolling_area())
+
+            widget.onMouseWheel = main_scrollbar.onMouseWheel
+
 class Scrolling_Area(Frame):
-    OS = platform.system()
 
-    def __init__(self, root, mousewheel_speed = 2, scroll_horizontally=True, scroll_vertically=True, outer_background=None, inner_frame=Frame, **kw):
-        if scroll_horizontally==False and scroll_vertically == False:
-            raise Exception("No direction for scrolling indicated")
-
-        Frame.__init__(self, root)
+    def __init__(self, master, mousewheel_speed = 2, scroll_horizontally=True, scroll_vertically=True, outer_background=None, inner_frame=Frame, **kw):
+        Frame.__init__(self, master)
         
         if outer_background:
             self.configure(background=outer_background)
-        
-        self._active_area = None
-        
-        if type(mousewheel_speed) == int:
-            self.mousewheel_speed = mousewheel_speed
-        else:
-            raise Exception("mousewheel_speed must be an integer.")
-
-        _onMouseWheel = lambda event: self._active_area._onMouseWheel(event) if self._active_area else None
-        
-        if self.OS == "Linux" :
-            root.bind_all('<4>', _onMouseWheel,  add='+')
-            root.bind_all('<5>', _onMouseWheel,  add='+')
-        else:
-            # Windows and MacOS
-            root.bind_all("<MouseWheel>", _onMouseWheel,  add='+')
 
         self._canvas = Canvas(self, highlightthickness=0)
         self._canvas.grid(row=0, column=0, sticky="nsew")
+
+        if scroll_vertically:
+            self.yscrollbar = Scrollbar(self, orient=VERTICAL)
+            self.yscrollbar.grid(row=0, column=1,sticky=N+S)
+        
+            self._canvas.configure(yscrollcommand=self.yscrollbar.set)
+            self.yscrollbar['command']=self._canvas.yview
+        else:
+            self.yscrollbar = None
+
+        if scroll_horizontally:
+            self.xscrollbar = Scrollbar(self, orient=HORIZONTAL)
+            self.xscrollbar.grid(row=1, column=0, sticky=E+W)
+            
+            self._canvas.configure(xscrollcommand=self.xscrollbar.set)
+            self.xscrollbar['command']=self._canvas.xview
+        else:
+            self.xscrollbar = None
 
         self.rowconfigure( 0, weight=1)
         self.columnconfigure(0, weight=1)
@@ -55,80 +138,16 @@
         
         self._canvas.create_window(0, 0, window=self.innerFrame, anchor='nw', tags="inner_frame")
                     
-        self._canvas.bind('<Configure>', self._configure_canvas)
+        self._canvas.bind('<Configure>', self._on_configure_canvas)
 
-        if scroll_vertically:
-            self.vscrollbar = Scrollbar(self, orient=VERTICAL)
-            self.vscrollbar._onMouseWheel = self._build_function_onMouseWheel('y')
-            
-            self.vscrollbar.grid(row=0, column=1,sticky=N+S)
+        Mousewheel_Support(self).add_support_to(self._canvas, xscrollbar=self.xscrollbar, yscrollbar=self.yscrollbar)
+
+    def _on_configure_canvas(self, event):
+        width = max(self.innerFrame.winfo_reqwidth(), event.width)
+        height = max(self.innerFrame.winfo_reqheight(), event.height)
         
-            self._canvas.configure(yscrollcommand=self.vscrollbar.set)
-            self.vscrollbar['command']=self._canvas.yview
-        else:
-            self.vscrollbar = None
-
-        if scroll_horizontally:
-            self.hscrollbar = Scrollbar(self, orient=HORIZONTAL)
-            self.hscrollbar._onMouseWheel = self._build_function_onMouseWheel('x')
-            
-            self.hscrollbar.grid(row=1, column=0, sticky=E+W)
-            
-            self._canvas.configure(xscrollcommand=self.hscrollbar.set)
-            
-            self.hscrollbar['command']=self._canvas.xview
-        else:
-            self.hscrollbar = None
-            
-        self._canvas.bind('<Enter>',lambda event: self._set_active_area(self._canvas))
-        self._canvas.bind('<Leave>', lambda event: self._unset_active_area())
-
-        if self.vscrollbar:
-            self.main_scrollbar = self.vscrollbar
-        else:
-            self.main_scrollbar = self.hscrollbar
-        
-        self._canvas._onMouseWheel = self.main_scrollbar._onMouseWheel
-
-        for scrollbar in (self.hscrollbar, self.vscrollbar):
-            if scrollbar is not None:
-                scrollbar.bind('<Enter>', lambda event, scrollbar=scrollbar: self._set_active_area(scrollbar) )
-                scrollbar.bind('<Leave>', lambda event: self._unset_active_area())
-
-    def _configure_canvas(self, event):
-        width = max(self.innerFrame.winfo_reqwidth(),self._canvas.winfo_width())
-        height = max(self.innerFrame.winfo_reqheight(),self._canvas.winfo_height())
-        
-        self._canvas.config(scrollregion="0 0 %s %s" % (width, height))
-        
-        self._canvas.itemconfigure("inner_frame", width=width)
-        self._canvas.itemconfigure("inner_frame", height=height)
-    
-    def _set_active_area(self, widget):
-        self._active_area = widget
-
-    def _unset_active_area(self):
-        self._active_area = None
-
-    def _build_function_onMouseWheel(self, orient):
-        view_command = getattr(self._canvas, orient+'view')
-        
-        if self.OS == 'Linux':
-            def onMouseWheel(event):                
-                if event.num == 4:
-                    view_command("scroll",(-1)*self.mousewheel_speed,"units" )
-                elif event.num == 5:
-                    view_command("scroll",self.mousewheel_speed,"units" ) 
-                
-        elif self.OS == 'Windows':
-            def onMouseWheel(event):        
-                view_command("scroll",(-1)*int((event.delta/120)*self.mousewheel_speed),"units" ) 
-        
-        elif self.OS == 'Darwin':
-            def onMouseWheel(event):        
-                view_command("scroll",event.delta,"units" )             
-        
-        return onMouseWheel
+        self._canvas.configure(scrollregion="0 0 %s %s" % (width, height))        
+        self._canvas.itemconfigure("inner_frame", width=width, height=height)
 
 if __name__== '__main__':
     try:

History