Skip to content

Commit

Permalink
Merge branch 'mousewheelscroll'. fixes #90
Browse files Browse the repository at this point in the history
  • Loading branch information
alejandroautalan committed Mar 25, 2017
2 parents 9b56d2c + 4acbcc0 commit 139cc74
Show file tree
Hide file tree
Showing 14 changed files with 553 additions and 72 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ MANIFEST
TODO
pygubu.nja
pygubu.egg-info/
myvenv/
9 changes: 5 additions & 4 deletions examples/scrollbarhelper.ui
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<?xml version="1.0" ?>
<?xml version='1.0' encoding='utf-8'?>
<interface>
<object class="ttk.Frame" id="mainwindow">
<layout>
<property name="column">0</property>
<property name="sticky">nsew</property>
<property name="row">0</property>
<property name="sticky">nsew</property>
<rows>
<row id="1">
<property name="weight">1</property>
Expand All @@ -18,7 +18,7 @@
</layout>
<child>
<object class="ttk.Label" id="label1">
<property name="text">ScrollbarHelper test:</property>
<property name="text" translatable="yes">ScrollbarHelper test:</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
Expand All @@ -28,10 +28,11 @@
<child>
<object class="pygubu.builder.widgets.scrollbarhelper" id="sbh1">
<property name="scrolltype">both</property>
<property name="usemousewheel">false</property>
<layout>
<property name="column">0</property>
<property name="sticky">nsew</property>
<property name="row">1</property>
<property name="sticky">nsew</property>
<rows>
<row id="0">
<property name="weight">1</property>
Expand Down
4 changes: 3 additions & 1 deletion pygubu/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from __future__ import unicode_literals

__all__ = ['Builder', 'TkApplication', 'BuilderObject', 'register_widget',
'register_property']
'register_property', 'remove_binding', 'ApplicationLevelBindManager']

import pygubu.builder.builderobject
from pygubu.builder import Builder
from pygubu.builder.builderobject import BuilderObject, register_widget
from pygubu.binding import remove_binding, ApplicationLevelBindManager


__version__ = '0.9.8'
Expand All @@ -15,6 +16,7 @@ def register_property(name, description):
return pygubu.builder.builderobject.register_property(name, description)



class TkApplication:
def __init__(self, master=None):
self.master = master
Expand Down
101 changes: 101 additions & 0 deletions pygubu/binding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from __future__ import unicode_literals

__all__ = ['remove_binding', 'ApplicationLevelBindManager']

import platform
import logging


logger = logging.getLogger(__name__)

def bindings(widget, seq):
return [x for x in widget.bind(seq).splitlines() if x.strip()]

def _funcid(binding):
return binding.split()[1][3:]

def remove_binding(widget, seq, index=None, funcid=None):
b = bindings(widget, seq)

if not index is None:
try:
binding = b[index]
widget.unbind(seq, _funcid(binding))
b.remove(binding)
except IndexError:
logger.info('Binding #%d not defined.' % index)
return

elif funcid:
binding = None
for x in b:
if _funcid(x) == funcid:
binding = x
b.remove(binding)
widget.unbind(seq, funcid)
break
if not binding:
logger.info( 'Binding "%s" not defined.' % funcid)
return
else:
raise ValueError('No index or function id defined.')

for x in b:
widget.bind(seq, '+'+x, 1)


class ApplicationLevelBindManager(object):
# Mouse wheel support
mw_active_area = None
mw_initialized = False

@staticmethod
def on_mousewheel(event):
if ApplicationLevelBindManager.mw_active_area:
ApplicationLevelBindManager.mw_active_area.on_mousewheel(event)

@staticmethod
def mousewheel_bind(widget):
ApplicationLevelBindManager.mw_active_area = widget

@staticmethod
def mousewheel_unbind():
ApplicationLevelBindManager.mw_active_area = None

@staticmethod
def init_mousewheel_binding(master):
if ApplicationLevelBindManager.mw_initialized == False:
_os = platform.system()
if _os == "Linux" :
master.bind_all('<4>', ApplicationLevelBindManager.on_mousewheel, add='+')
master.bind_all('<5>', ApplicationLevelBindManager.on_mousewheel, add='+')
else:
# Windows and MacOS
master.bind_all("<MouseWheel>", ApplicationLevelBindManager.on_mousewheel, add='+')
ApplicationLevelBindManager.mw_initialized = True

@staticmethod
def make_onmousewheel_cb(widget, orient, factor = 1):
"""Create a callback to manage mousewheel events
orient: string (posible values: ('x', 'y'))
widget: widget that implement tk xview and yview methods
"""
_os = platform.system()
view_command = getattr(widget, orient+'view')
if _os == 'Linux':
def on_mousewheel(event):
if event.num == 4:
view_command("scroll",(-1)*factor,"units")
elif event.num == 5:
view_command("scroll",factor,"units")

elif _os == 'Windows':
def on_mousewheel(event):
view_command("scroll",(-1)*int((event.delta/120)*factor),"units")

elif _os == 'Darwin':
def on_mousewheel(event):
view_command("scroll",event.delta,"units")

return on_mousewheel
4 changes: 2 additions & 2 deletions pygubu/builder/widgets/scrollbarhelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ class TTKSBHelperBO(BuilderObject):
'tk.Listbox', 'ttk.Treeview' )
OPTIONS_STANDARD = ('class_', 'cursor', 'takefocus', 'style')
OPTIONS_SPECIFIC = ('borderwidth', 'relief', 'padding', 'height', 'width')
OPTIONS_CUSTOM = ('scrolltype', )
OPTIONS_CUSTOM = ('scrolltype', 'usemousewheel')
properties = OPTIONS_STANDARD + OPTIONS_SPECIFIC + OPTIONS_CUSTOM
ro_properties = ('class_', 'scrolltype', )
ro_properties = ('class_', 'scrolltype')
allow_bindings = False


Expand Down
8 changes: 7 additions & 1 deletion pygubu/builder/widgets/scrolledframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class TTKScrolledFrameBO(BuilderObject):
# allowed_children = ('tk.Frame', 'ttk.Frame' )
OPTIONS_STANDARD = ('class_', 'cursor', 'takefocus', 'style')
OPTIONS_SPECIFIC = ('borderwidth', 'relief', 'padding', 'height', 'width')
OPTIONS_CUSTOM = ('scrolltype', )
OPTIONS_CUSTOM = ('scrolltype', 'usemousewheel')
properties = OPTIONS_STANDARD + OPTIONS_SPECIFIC + OPTIONS_CUSTOM
ro_properties = ('class_', 'scrolltype', )

Expand All @@ -21,6 +21,12 @@ def get_child_master(self):
def configure(self, target=None):
super(TTKScrolledFrameBO, self).configure(self.widget.innerframe)

def _set_property(self, target_widget, pname, value):
if pname in ('usemousewheel',):
super(TTKScrolledFrameBO, self)._set_property(self.widget, pname, value)
else:
super(TTKScrolledFrameBO, self)._set_property(target_widget, pname, value)

def layout(self, target=None):
self._grid_layout(self.widget, configure_rc=False)
self._grid_rc_layout(self.widget.innerframe)
Expand Down
4 changes: 2 additions & 2 deletions pygubu/builder/widgets/tkscrollbarhelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ class TKSBHelperBO(BuilderObject):
'padx', 'pady', 'relief', 'takefocus')
OPTIONS_SPECIFIC = ('background', 'class_', 'container',
'height', 'width')
OPTIONS_CUSTOM = ('scrolltype',)
OPTIONS_CUSTOM = ('scrolltype', 'usemousewheel')
properties = OPTIONS_STANDARD + OPTIONS_SPECIFIC + OPTIONS_CUSTOM
ro_properties = ('class_', 'scrolltype', )
ro_properties = ('class_', 'scrolltype')
allow_bindings = False

def add_child(self, bobject):
Expand Down
8 changes: 7 additions & 1 deletion pygubu/builder/widgets/tkscrolledframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class TKScrolledFrameBO(BuilderObject):
'padx', 'pady', 'relief', 'takefocus')
OPTIONS_SPECIFIC = ('background', 'class_', 'container',
'height', 'width')
OPTIONS_CUSTOM = ('scrolltype',)
OPTIONS_CUSTOM = ('scrolltype', 'usemousewheel')
properties = OPTIONS_STANDARD + OPTIONS_SPECIFIC + OPTIONS_CUSTOM
ro_properties = ('class_', 'scrolltype')

Expand All @@ -23,6 +23,12 @@ def get_child_master(self):

def configure(self, target=None):
super(TKScrolledFrameBO, self).configure(self.widget.innerframe)

def _set_property(self, target_widget, pname, value):
if pname in ('usemousewheel',):
super(TKScrolledFrameBO, self)._set_property(self.widget, pname, value)
else:
super(TKScrolledFrameBO, self)._set_property(target_widget, pname, value)

def layout(self, target=None):
self._grid_layout(self.widget, configure_rc=False)
Expand Down
71 changes: 68 additions & 3 deletions pygubu/widgets/tkscrollbarhelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@
# For further info, check https://github.com/alejandroautalan/pygubu

from __future__ import unicode_literals

import logging
try:
import tkinter as tk
except:
import Tkinter as tk
from pygubu import ApplicationLevelBindManager as BindManager


logger = logging.getLogger(__name__)

def _autoscroll(sbar, first, last):
"""Hide and show scrollbar as needed.
Expand All @@ -43,7 +47,12 @@ class ScrollbarHelper(frame_class, object):

def __init__(self, master=None, **kw):
self.scrolltype = kw.pop('scrolltype', self.VERTICAL)
self.usemousewheel = tk.getboolean(kw.pop('usemousewheel', False))
super(ScrollbarHelper, self).__init__(master, **kw)
self.vsb = None
self.hsb = None
self.cwidget = None
self._bindingids = []
self._create_scrollbars()

def _create_scrollbars(self):
Expand All @@ -60,6 +69,7 @@ def _create_scrollbars(self):
self.grid_rowconfigure(0, weight=1)

def add_child(self, cwidget):
self.cwidget = cwidget
cwidget.grid(column=0, row=0, sticky=tk.NSEW, in_=self)

if self.scrolltype in (self.BOTH, self.VERTICAL):
Expand All @@ -68,15 +78,70 @@ def add_child(self, cwidget):
cwidget.configure(yscrollcommand=lambda f, l: _autoscroll(self.vsb, f, l))
else:
msg = "widget {} has no attribute 'yview'".format(str(cwidget))
logger.warning(msg)
logger.info(msg)

if self.scrolltype in (self.BOTH, self.HORIZONTAL):
if hasattr(cwidget, 'xview'):
self.hsb.configure(command=cwidget.xview)
cwidget.configure(xscrollcommand=lambda f, l: _autoscroll(self.hsb, f, l))
else:
msg = "widget {} has no attribute 'xview'".format(str(cwidget))
logger.warning(msg)
logger.info(msg)
self._configure_mousewheel()

def configure(self, cnf=None, **kw):
args = tk._cnfmerge((cnf, kw))
key = 'usemousewheel'
if key in args:
self.usemousewheel = tk.getboolean(args[key])
del args[key]
self._configure_mousewheel()
frame_class.configure(self, args)

config = configure

def cget(self, key):
option = 'usemousewheel'
if key == option:
return self.usemousewheel
return frame_class.cget(self, key)

__getitem__ = cget

def _configure_mousewheel(self):
cwidget = self.cwidget
if self.usemousewheel:
BindManager.init_mousewheel_binding(self)

if self.hsb and not hasattr(self.hsb, 'on_mousewheel'):
self.hsb.on_mousewheel = BindManager.make_onmousewheel_cb(cwidget, 'x', 2)
if self.vsb and not hasattr(self.vsb, 'on_mousewheel'):
self.vsb.on_mousewheel = BindManager.make_onmousewheel_cb(cwidget, 'y', 2)

main_sb = self.vsb or self.hsb
if main_sb:
cwidget.on_mousewheel = main_sb.on_mousewheel
bid = cwidget.bind('<Enter>',
lambda event: BindManager.mousewheel_bind(cwidget),
add='+')
self._bindingids.append((cwidget, bid))
bid = cwidget.bind('<Leave>',
lambda event: BindManager.mousewheel_unbind(),
add='+')
self._bindingids.append((cwidget, bid))
for s in (self.vsb, self.hsb):
if s:
bid = s.bind('<Enter>',
lambda event, scrollbar=s: BindManager.mousewheel_bind(scrollbar),
add='+')
self._bindingids.append((s, bid))
bid = s.bind('<Leave>',
lambda event: BindManager.mousewheel_unbind(),
add='+')
self._bindingids.append((s, bid))
else:
for widget, bid in self._bindingids:
remove_binding(widget, bid)

return ScrollbarHelper

Expand Down
Loading

0 comments on commit 139cc74

Please sign in to comment.