def scroll_with_mousewheel(widget, target=None, modifier='Shift', apply_to_children=False):
    """scroll a widget with mouse wheel

    Args:
        widget: tkinter widget
        target: scrollable tkinter widget, in case you need "widget" to catch mousewheel event and make another widget
                to scroll, useful for child widget in a scrollable frame
        modifier (str): Modifier to use with mousewheel to scroll horizontally, default is shift key
        apply_to_children (bool): bind all children

    Examples:
        scroll_with_mousewheel(my_text_widget, target='my_scrollable_frame')

        to make a scrollable canvas:
        for w in my_canvas:
            scroll_with_mousewheel(w, target=my_canvas)
    """

    def _scroll_with_mousewheel(widget):

        target_widget = target if target else widget

        def scroll_vertically(event):
            # scroll vertically  ----------------------------------
            if event.num == 4 or event.delta > 0:
                target_widget.yview_scroll(-1, "unit")

            elif event.num == 5 or event.delta < 0:
                target_widget.yview_scroll(1, "unit")

            return 'break'

        # bind events for vertical scroll ----------------------------------------------
        if hasattr(target_widget, 'yview_scroll'):
            # linux
            widget.bind("<Button-4>", scroll_vertically, add='+')
            widget.bind("<Button-5>", scroll_vertically, add='+')

            # windows and mac
            widget.bind("<MouseWheel>", scroll_vertically, add='+')

        # scroll horizontally ---------------------------------------
        def scroll_horizontally(event):
            # scroll horizontally
            if event.num == 4 or event.delta > 0:
                target_widget.xview_scroll(-10, "unit")

            elif event.num == 5 or event.delta < 0:
                target_widget.xview_scroll(10, "unit")

            return 'break'

        # bind events for horizontal scroll ----------------------------------------------
        if hasattr(target_widget, 'xview_scroll'):
            # linux
            widget.bind(f"<{modifier}-Button-4>", scroll_horizontally, add='+')
            widget.bind(f"<{modifier}-Button-5>", scroll_horizontally, add='+')

            # windows and mac
            widget.bind(f"<{modifier}-MouseWheel>", scroll_horizontally, add='+')

    _scroll_with_mousewheel(widget)

    def handle_children(w):
        for child in w.winfo_children():
            _scroll_with_mousewheel(child)

            # recursive call
            if child.winfo_children():
                handle_children(child)

    if apply_to_children:
        handle_children(widget)