# Version: 0.22 # 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() class Mousewheel_Support(object): # implemetation of singleton pattern _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = object.__new__(cls) 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_mousewheel, add='+') root.bind_all('<5>', self._on_mousewheel, add='+') else: # Windows and MacOS root.bind_all("", self._on_mousewheel, add='+') def _on_mousewheel(self,event): if self._active_area: self._active_area.onMouseWheel(event) def _mousewheel_bind(self, widget): self._active_area = widget def _mousewheel_unbind(self): self._active_area = None def add_support_to(self, widget=None, xscrollbar=None, yscrollbar=None, what="units", horizontal_factor=None, vertical_factor=None): if xscrollbar is None and yscrollbar is None: return if xscrollbar is not None: horizontal_factor = horizontal_factor or self.horizontal_factor xscrollbar.onMouseWheel = self._make_mouse_wheel_handler(widget,'x', self.horizontal_factor, what) xscrollbar.bind('', lambda event, scrollbar=xscrollbar: self._mousewheel_bind(scrollbar) ) xscrollbar.bind('', lambda event: self._mousewheel_unbind()) if yscrollbar is not None: vertical_factor = vertical_factor or self.vertical_factor yscrollbar.onMouseWheel = self._make_mouse_wheel_handler(widget,'y', self.vertical_factor, what) yscrollbar.bind('', lambda event, scrollbar=yscrollbar: self._mousewheel_bind(scrollbar) ) yscrollbar.bind('', lambda event: self._mousewheel_unbind()) main_scrollbar = yscrollbar if yscrollbar is not None else xscrollbar if widget is not None: if isinstance(widget, list) or isinstance(widget, tuple): list_of_widgets = widget for widget in list_of_widgets: widget.bind('',lambda event: self._mousewheel_bind(widget)) widget.bind('', lambda event: self._mousewheel_unbind()) widget.onMouseWheel = main_scrollbar.onMouseWheel else: widget.bind('',lambda event: self._mousewheel_bind(widget)) widget.bind('', lambda event: self._mousewheel_unbind()) widget.onMouseWheel = main_scrollbar.onMouseWheel @staticmethod def _make_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 Scrolling_Area(Frame, object): def __init__(self, master, width=None, anchor=N, height=None, mousewheel_speed = 2, scroll_horizontally=True, xscrollbar=None, scroll_vertically=True, yscrollbar=None, background=None, inner_frame=Frame, **kw): Frame.__init__(self, master, class_="Scrolling_Area", background=background) self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self._width = width self._height = height self.canvas = Canvas(self, background=background, highlightthickness=0, width=width, height=height) self.canvas.grid(row=0, column=0, sticky=N+E+W+S) if scroll_vertically: if yscrollbar is not None: self.yscrollbar = yscrollbar else: 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: if xscrollbar is not None: self.xscrollbar = xscrollbar else: 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(anchor=anchor) self.canvas.create_window(0, 0, window=self.innerframe, anchor='nw', tags="inner_frame") self.canvas.bind('', self._on_canvas_configure) Mousewheel_Support(self).add_support_to(self.canvas, xscrollbar=self.xscrollbar, yscrollbar=self.yscrollbar) @property def width(self): return self.canvas.winfo_width() @width.setter def width(self, width): self.canvas.configure(width= width) @property def height(self): return self.canvas.winfo_height() @height.setter def height(self, height): self.canvas.configure(height = height) def set_size(self, width, height): self.canvas.configure(width=width, height = height) def _on_canvas_configure(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) def update_viewport(self): self.update() window_width = self.innerframe.winfo_reqwidth() window_height = self.innerframe.winfo_reqheight() if self._width is None: canvas_width = window_width else: canvas_width = min(self._width, window_width) if self._height is None: canvas_height = window_height else: canvas_height = min(self._height, window_height) self.canvas.configure(scrollregion="0 0 %s %s" % (window_width, window_height), width=canvas_width, height=canvas_height) self.canvas.itemconfigure("inner_frame", width=window_width, height=window_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()