Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qt5 Support #302

Merged
merged 4 commits into from
Apr 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ addons:
- python-wxtools
- ccache
- cmake
- swig
- zlib1g-dev
- libpng-dev
- libfreetype6-dev
Expand All @@ -26,7 +25,9 @@ matrix:
- env: RUNTIME=2.7 TOOLKIT=wx PILLOW='pillow'
- env: RUNTIME=2.7 TOOLKIT=pyqt PILLOW='pillow'
- env: RUNTIME=3.5 TOOLKIT=pyqt PILLOW='pillow'
- env: RUNTIME=3.5 TOOLKIT=pyqt5 PILLOW='pillow'
- env: RUNTIME=3.6 TOOLKIT=pyqt PILLOW='pillow'
- env: RUNTIME=3.6 TOOLKIT=pyqt5 PILLOW='pillow'
- env: RUNTIME=2.7 TOOLKIT=null PILLOW='pillow'
- env: RUNTIME=3.5 TOOLKIT=null PILLOW='pillow'
- env: RUNTIME=3.6 TOOLKIT=null PILLOW='pillow'
Expand Down
15 changes: 12 additions & 3 deletions ci/edmtool.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@

supported_combinations = {
'2.7': {'pyqt', 'wx', 'null'},
'3.5': {'pyqt', 'null'},
'3.6': {'pyqt', 'null'},
'3.5': {'pyqt', 'pyqt5', 'null'},
'3.6': {'pyqt', 'pyqt5', 'null'},
}

dependencies = {
Expand All @@ -104,16 +104,19 @@
"pyparsing",
"unittest2",
"pypdf2",
"swig",
}

extra_dependencies = {
'pyqt': {'pyqt'},
'pyqt5': set(),
'wx': {'wxpython'},
'null': set()
}

environment_vars = {
'pyqt': {'ETS_TOOLKIT': 'qt4', 'QT_API': 'pyqt'},
'pyqt5': {'ETS_TOOLKIT': 'qt4', 'QT_API': 'pyqt5'},
'wx': {'ETS_TOOLKIT': 'wx'},
'null': {'ETS_TOOLKIT': 'null.image'},
}
Expand Down Expand Up @@ -152,8 +155,14 @@ def install(runtime, toolkit, pillow, environment):
"edm run -e {environment} -- pip install {pillow}",
("edm run -e {environment} -- pip install -r ci/requirements.txt"
" --no-dependencies"),
"edm run -e {environment} -- python setup.py install",
]

# pip install pyqt5, because we don't have it in EDM yet
if toolkit == 'pyqt5':
commands.append("edm run -e {environment} -- pip install pyqt5==5.9.2")

commands.append("edm run -e {environment} -- python setup.py install")

click.echo("Creating environment '{environment}'".format(**parameters))
execute(commands, parameters)
click.echo('Done install')
Expand Down
1 change: 1 addition & 0 deletions enable/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class MouseEvent(BasicEvent):
right_down = ReadOnly
mouse_wheel = ReadOnly
mouse_wheel_axis = ReadOnly
mouse_wheel_delta = ReadOnly

mouse_event_trait = Event(MouseEvent)

Expand Down
26 changes: 23 additions & 3 deletions enable/qt4/base_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
DRAG_RESULTS_MAP
)


is_qt4 = (QtCore.__version_info__[0] <= 4)


class _QtWindowHandler(object):
def __init__(self, qt_window, enable_window):
self._enable_window = enable_window
Expand Down Expand Up @@ -408,19 +412,35 @@ def _create_mouse_event(self, event):
# A bit crap, because AbstractWindow was written with wx in mind, and
# we treat wheel events like mouse events.
if isinstance(event, QtGui.QWheelEvent):
delta = event.delta()
degrees_per_step = 15.0
mouse_wheel = delta / float(8 * degrees_per_step)
mouse_wheel_axis = MOUSE_WHEEL_AXIS_MAP[event.orientation()]
if is_qt4:
delta = event.delta()
mouse_wheel = delta / float(8 * degrees_per_step)
mouse_wheel_axis = MOUSE_WHEEL_AXIS_MAP[event.orientation()]
if mouse_wheel_axis == 'horizontal':
mouse_wheel_delta = (delta, 0)
else:
mouse_wheel_delta = (0, delta)
else:
delta = event.pixelDelta()
mouse_wheel_delta = (delta.x(), delta.y())
if abs(mouse_wheel_delta[0]) > abs(mouse_wheel_delta[1]):
mouse_wheel = mouse_wheel_delta[0] / float(8 * degrees_per_step)
mouse_wheel_axis = 'horizontal'
else:
mouse_wheel = mouse_wheel_delta[1] / float(8 * degrees_per_step)
mouse_wheel_axis = 'vertical'
else:
mouse_wheel = 0
mouse_wheel_delta = (0, 0)
mouse_wheel_axis = 'vertical'

return MouseEvent(
x=x,
y=self._flip_y(y),
mouse_wheel=mouse_wheel,
mouse_wheel_axis=mouse_wheel_axis,
mouse_wheel_delta=mouse_wheel_delta,
alt_down=bool(modifiers & QtCore.Qt.AltModifier),
shift_down=bool(modifiers & QtCore.Qt.ShiftModifier),
control_down=bool(modifiers & QtCore.Qt.ControlModifier),
Expand Down
6 changes: 6 additions & 0 deletions enable/qt4/scrollbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ def valid_scroll_position(object, name, value):


class QResizableScrollBar(QtGui.QScrollBar):

resized = QtCore.Signal()

def resizeEvent(self, event):
super(QResizableScrollBar, self).resizeEvent(event)
self.resized.emit()
Expand Down Expand Up @@ -175,6 +177,7 @@ def _create_control(self, window, enable_range, value):
self._control.sliderPressed.connect(self._on_slider_pressed)
self._control.sliderReleased.connect(self._on_slider_released)
self._control.resized.connect(self._control_resized)
self._control.destroyed.connect(self._on_destroyed)
self._control.setVisible(True)

def _update_control(self, enable_range, value):
Expand Down Expand Up @@ -231,6 +234,9 @@ def _control_resized(self):
self._widget_moved = True
self.request_redraw()

def _on_destroyed(self):
self._control = None

#------------------------------------------------------------------------
# Basic trait event handlers
#------------------------------------------------------------------------
Expand Down
40 changes: 32 additions & 8 deletions enable/tests/qt4/mouse_wheel_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,31 +43,55 @@ def setUp(self):
def test_vertical_mouse_wheel(self):
from pyface.qt import QtCore, QtGui

is_qt4 = (QtCore.__version_info__[0] <= 4)

# create and mock a mouse wheel event
qt_event = QtGui.QWheelEvent(
QtCore.QPoint(0, 0), 200, QtCore.Qt.NoButton, QtCore.Qt.NoModifier,
QtCore.Qt.Vertical
)
if is_qt4:
qt_event = QtGui.QWheelEvent(
QtCore.QPoint(0, 0), 200, QtCore.Qt.NoButton,
QtCore.Qt.NoModifier, QtCore.Qt.Vertical
)
else:
qt_event = QtGui.QWheelEvent(
QtCore.QPointF(0, 0),
self.window.control.mapToGlobal(QtCore.QPoint(0, 0)),
QtCore.QPoint(0, 200), QtCore.QPoint(0, 200.0/120), 200,
QtCore.Qt.Vertical, QtCore.Qt.NoButton, QtCore.Qt.NoModifier,
QtCore.Qt.ScrollUpdate
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this code should be in a pyface testing support module... But I don't see any such module in pyface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pyface doesn't really do any testing at the level of individual mouse events (things are usually driven at the level of signals and slots), so we haven't had a need to put anything like this into Pyface.

For now it's probably good enough to have it as part of enable.tests.qt4; if we find that we are repeating this a lot we might want to create a enable.qt4.testing.* utility method.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me


# dispatch event
self.window._on_mouse_wheel(qt_event)

# validate results
self.assertEqual(self.tool.event.mouse_wheel_axis, 'vertical')
self.assertAlmostEqual(self.tool.event.mouse_wheel, 5.0/3.0)
self.assertEqual(self.tool.event.mouse_wheel_delta, (0, 200))

def test_horizontal_mouse_wheel(self):
from pyface.qt import QtCore, QtGui

is_qt4 = (QtCore.__version_info__[0] <= 4)

# create and mock a mouse wheel event
qt_event = QtGui.QWheelEvent(
QtCore.QPoint(0, 0), 200, QtCore.Qt.NoButton, QtCore.Qt.NoModifier,
QtCore.Qt.Horizontal
)
if is_qt4:
qt_event = QtGui.QWheelEvent(
QtCore.QPoint(0, 0), 200, QtCore.Qt.NoButton,
QtCore.Qt.NoModifier, QtCore.Qt.Horizontal
)
else:
qt_event = QtGui.QWheelEvent(
QtCore.QPoint(0, 0),
self.window.control.mapToGlobal(QtCore.QPoint(0, 0)),
QtCore.QPoint(200, 0), QtCore.QPoint(200.0/120, 0), 200,
QtCore.Qt.Vertical, QtCore.Qt.NoButton, QtCore.Qt.NoModifier,
QtCore.Qt.ScrollUpdate
)

# dispatch event
self.window._on_mouse_wheel(qt_event)

# validate results
self.assertEqual(self.tool.event.mouse_wheel_axis, 'horizontal')
self.assertAlmostEqual(self.tool.event.mouse_wheel, 5.0/3.0)
self.assertEqual(self.tool.event.mouse_wheel_delta, (200, 0))
2 changes: 2 additions & 0 deletions enable/tests/wx/mouse_wheel_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def test_vertical_mouse_wheel(self):
# validate results
self.assertEqual(self.tool.event.mouse_wheel_axis, 'vertical')
self.assertAlmostEqual(self.tool.event.mouse_wheel, 5.0/3.0)
self.assertEqual(self.tool.event.mouse_wheel_delta, (0, 200))

def test_horizontal_mouse_wheel(self):
import wx
Expand All @@ -78,3 +79,4 @@ def test_horizontal_mouse_wheel(self):
# validate results
self.assertEqual(self.tool.event.mouse_wheel_axis, 'horizontal')
self.assertAlmostEqual(self.tool.event.mouse_wheel, 5.0/3.0)
self.assertEqual(self.tool.event.mouse_wheel_delta, (200, 0))
4 changes: 4 additions & 0 deletions enable/wx/base_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,10 @@ def _create_mouse_event ( self, event ):
event.GetWheelRotation()) /
(event.GetWheelDelta() or 1))
wheel_axis = MOUSE_WHEEL_AXIS_MAP[event.GetWheelAxis()]
if wheel_axis == 'horizontal':
mouse_wheel_delta = (mouse_wheel * 120/200., 0)
else:
mouse_wheel_delta = (0, mouse_wheel * 120/200.)

# Note: The following code fixes a bug in wxPython that returns
# 'mouse_wheel' events in screen coordinates, rather than window
Expand Down