From 415b0111e462fd2258b58d78d16bc1e01c975916 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 20 Aug 2015 09:49:22 +0100 Subject: [PATCH 1/9] Fix scrolling problem with Qt native scrollbar. --- enable/qt4/scrollbar.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/enable/qt4/scrollbar.py b/enable/qt4/scrollbar.py index f8cd4fe0e..c2b35d2b1 100644 --- a/enable/qt4/scrollbar.py +++ b/enable/qt4/scrollbar.py @@ -183,7 +183,11 @@ def _update_control(self, enable_range, value): # scroll bar, not the document length. We need to subtract the length # of the scroll bar itself. self._control.setMaximum(maximum-page_size) - self._control.setValue(value) + # invert values for vertical ranges because of coordinate system issues + if self.orientation == 'vertical': + self._control.setValue(maximum-page_size-value) + else: + self._control.setValue(value) self._control.setPageStep(page_size) self._control.setSingleStep(line_size) @@ -192,7 +196,11 @@ def _update_control(self, enable_range, value): #------------------------------------------------------------------------ def _update_enable_pos(self, value): - self.scroll_position = value + # invert values for vertical ranges because of coordinate system issues + if self.orientation == "vertical": + self.scroll_position = self.high - self.page_size - value + else: + self.scroll_position = value def _on_slider_pressed(self): self.mouse_thumb = "down" @@ -285,4 +293,3 @@ def _set_line_size(self, line_size): low, high, page_size, ignore = self.range self._clean = False self.range =(low, high, page_size, line_size) - From 5151669bc478d3c4c4189b7440a1710889672486 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 21 Aug 2015 10:08:46 +0100 Subject: [PATCH 2/9] Refactor to create common utility routine. --- enable/qt4/scrollbar.py | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/enable/qt4/scrollbar.py b/enable/qt4/scrollbar.py index c2b35d2b1..5985b45b5 100644 --- a/enable/qt4/scrollbar.py +++ b/enable/qt4/scrollbar.py @@ -178,29 +178,47 @@ def _create_control(self, window, enable_range, value): def _update_control(self, enable_range, value): minimum, maximum, page_size, line_size = enable_range - self._control.setMinimum(minimum) # The maximum value of a QScrollBar is the maximum position of the # scroll bar, not the document length. We need to subtract the length # of the scroll bar itself. - self._control.setMaximum(maximum-page_size) + max_value = maximum-page_size # invert values for vertical ranges because of coordinate system issues - if self.orientation == 'vertical': - self._control.setValue(maximum-page_size-value) - else: - self._control.setValue(value) + value = self._correct_value(value, max_value) + + self._control.setMinimum(minimum) + self._control.setMaximum(max_value) + self._control.setValue(value) self._control.setPageStep(page_size) self._control.setSingleStep(line_size) + def _correct_value(self, value, max_value): + """ Correct vertical position values for Qt and Enable conventions + + Enable expects vertical scroll_position to be measured with origin at + the bottom and positive going upwards, while Qt scrollbar values are + measured with origin at the top and positive going down. + + Parameters + ---------- + value : float + The position value in either Enable or Qt conventions. + max_value : float + The maximum value that the Qt scrollbar can be set to (height of + the scrolled component, less the page size). + """ + if self.orientation != 'vertical': + return value + return max_value - value + + #------------------------------------------------------------------------ # Qt Event handlers #------------------------------------------------------------------------ def _update_enable_pos(self, value): # invert values for vertical ranges because of coordinate system issues - if self.orientation == "vertical": - self.scroll_position = self.high - self.page_size - value - else: - self.scroll_position = value + value = self._correct_value(value, self.high-self.page_size) + self.scroll_position = value def _on_slider_pressed(self): self.mouse_thumb = "down" From 7d6e5464b2119f9b2c8bd54d2bef4d271478a30c Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 30 Aug 2015 09:00:32 +0100 Subject: [PATCH 3/9] tests, plus corrections for non-zero min values --- enable/qt4/scrollbar.py | 8 +- enable/tests/qt4/__init__.py | 0 enable/tests/qt4/scrollbar_test.py | 131 +++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 enable/tests/qt4/__init__.py create mode 100644 enable/tests/qt4/scrollbar_test.py diff --git a/enable/qt4/scrollbar.py b/enable/qt4/scrollbar.py index 5985b45b5..79a048ebe 100644 --- a/enable/qt4/scrollbar.py +++ b/enable/qt4/scrollbar.py @@ -183,7 +183,7 @@ def _update_control(self, enable_range, value): # of the scroll bar itself. max_value = maximum-page_size # invert values for vertical ranges because of coordinate system issues - value = self._correct_value(value, max_value) + value = self._correct_value(value, minimum, max_value) self._control.setMinimum(minimum) self._control.setMaximum(max_value) @@ -191,7 +191,7 @@ def _update_control(self, enable_range, value): self._control.setPageStep(page_size) self._control.setSingleStep(line_size) - def _correct_value(self, value, max_value): + def _correct_value(self, value, min_value, max_value): """ Correct vertical position values for Qt and Enable conventions Enable expects vertical scroll_position to be measured with origin at @@ -208,7 +208,7 @@ def _correct_value(self, value, max_value): """ if self.orientation != 'vertical': return value - return max_value - value + return max_value - (value - min_value) #------------------------------------------------------------------------ @@ -217,7 +217,7 @@ def _correct_value(self, value, max_value): def _update_enable_pos(self, value): # invert values for vertical ranges because of coordinate system issues - value = self._correct_value(value, self.high-self.page_size) + value = self._correct_value(value, self.low, self.high-self.page_size) self.scroll_position = value def _on_slider_pressed(self): diff --git a/enable/tests/qt4/__init__.py b/enable/tests/qt4/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/enable/tests/qt4/scrollbar_test.py b/enable/tests/qt4/scrollbar_test.py new file mode 100644 index 000000000..e96ac33f3 --- /dev/null +++ b/enable/tests/qt4/scrollbar_test.py @@ -0,0 +1,131 @@ + +from contextlib import contextmanager +import unittest + +from enable.container import Container +from enable.qt4.scrollbar import NativeScrollBar +from enable.window import Window + + +class ScrollBarTest(unittest.TestCase): + + def setUp(self): + self.container = Container(position=[0, 0], bounds=[600, 600]) + self.window = Window(None, size=(600, 600), component=self.container) + + @contextmanager + def setup_window(self, window): + window.control.show() + try: + yield + finally: + window.control.destroy() + + @contextmanager + def setup_scrollbar(self, scrollbar, window, enable_range, value): + scrollbar._draw_mainlayer(self, window._gc) + try: + yield + finally: + scrollbar.destroy() + + def test_scroll_position_horizontal(self): + bounds = [600.0, 30.0] + position = [0.0, 0.0] + range = [600, 0.0, 375.0, 20.454545454545453] + scrollbar = NativeScrollBar( + orientation='horizontal', + bounds=bounds, + position=position, + range=range, + ) + self.container.add(scrollbar) + with self.setup_window(self.window): + with self.setup_scrollbar(scrollbar, self.window, range, 0): + self.assertEqual(scrollbar._control.value(), 0) + self.assertEqual(scrollbar.scroll_position, 0) + + # move the scrollbar + scrollbar._control.setValue(100) + self.assertEqual(scrollbar.scroll_position, 100) + + # set the scroll & redraw + scrollbar.scroll_position = 200 + scrollbar._draw_mainlayer(self, self.window._gc) + self.assertEqual(scrollbar._control.value(), 200) + + def test_scroll_position_vertical(self): + bounds = [30.0, 600.0] + position = [0.0, 0.0] + range = [600, 0.0, 375.0, 20.454545454545453] + scrollbar = NativeScrollBar( + orientation='vertical', + bounds=bounds, + position=position, + range=range, + ) + self.container.add(scrollbar) + with self.setup_window(self.window): + with self.setup_scrollbar(scrollbar, self.window, range, 0): + self.assertEqual(scrollbar._control.value(), 600-375) + self.assertEqual(scrollbar.scroll_position, 0) + + # move the scrollbar + scrollbar._control.setValue(100) + self.assertEqual(scrollbar.scroll_position, 600-375-100) + + # set the scroll & redraw + scrollbar.scroll_position = 200 + scrollbar._draw_mainlayer(self, self.window._gc) + self.assertEqual(scrollbar._control.value(), 600-375-200) + + def test_minumum_horizontal(self): + bounds = [600.0, 30.0] + position = [0.0, 0.0] + range = [700, 100.0, 375.0, 20.454545454545453] + scrollbar = NativeScrollBar( + orientation='horizontal', + bounds=bounds, + position=position, + range=range, + ) + self.container.add(scrollbar) + with self.setup_window(self.window): + with self.setup_scrollbar(scrollbar, self.window, range, 100): + self.assertEqual(scrollbar._control.value(), 100) + self.assertEqual(scrollbar.scroll_position, 100) + + # move the scrollbar + scrollbar._control.setValue(200) + self.assertEqual(scrollbar.scroll_position, 200) + + # set the scroll & redraw + scrollbar.scroll_position = 300 + scrollbar._draw_mainlayer(self, self.window._gc) + self.assertEqual(scrollbar._control.value(), 300) + + def test_minimum_vertical(self): + bounds = [30.0, 600.0] + position = [0.0, 0.0] + range = [700, 100.0, 375.0, 20.454545454545453] + scrollbar = NativeScrollBar( + orientation='vertical', + bounds=bounds, + position=position, + range=range, + ) + self.container.add(scrollbar) + with self.setup_window(self.window): + with self.setup_scrollbar(scrollbar, self.window, range, 100): + # control should be at top + self.assertEqual(scrollbar._control.value(), 700-375) + self.assertEqual(scrollbar.scroll_position, 100) + + # move the scrollbar to the bottom + scrollbar._control.setValue(100) + self.assertEqual(scrollbar.scroll_position, 700-375) + + # set the scroll & redraw + scrollbar.scroll_position = 200 + scrollbar._draw_mainlayer(self, self.window._gc) + self.assertEqual(scrollbar._control.value(), 700-375-(200-100)) From 79e061200c3cb466c8f415533abb999ec40856a0 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 30 Aug 2015 10:31:00 +0100 Subject: [PATCH 4/9] Skip new test if not under qt4 --- enable/tests/qt4/scrollbar_test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/enable/tests/qt4/scrollbar_test.py b/enable/tests/qt4/scrollbar_test.py index e96ac33f3..7c1a830dd 100644 --- a/enable/tests/qt4/scrollbar_test.py +++ b/enable/tests/qt4/scrollbar_test.py @@ -2,11 +2,14 @@ from contextlib import contextmanager import unittest +from traitsui.tests._tools import skip_if_not_qt4 + from enable.container import Container from enable.qt4.scrollbar import NativeScrollBar from enable.window import Window +@skip_if_not_qt4 class ScrollBarTest(unittest.TestCase): def setUp(self): From 58e27294452f166b37f4c768b348ca656062db55 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 30 Aug 2015 11:01:15 +0100 Subject: [PATCH 5/9] One last test for non-qt-ness. --- enable/tests/qt4/scrollbar_test.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/enable/tests/qt4/scrollbar_test.py b/enable/tests/qt4/scrollbar_test.py index 7c1a830dd..1bff0fe70 100644 --- a/enable/tests/qt4/scrollbar_test.py +++ b/enable/tests/qt4/scrollbar_test.py @@ -1,18 +1,24 @@ from contextlib import contextmanager -import unittest +from traits.testing.unittest_tools import unittest from traitsui.tests._tools import skip_if_not_qt4 from enable.container import Container -from enable.qt4.scrollbar import NativeScrollBar from enable.window import Window +try: + from enable.qt4.scrollbar import NativeScrollBar +except Exception: + NativeScrollBar = None + @skip_if_not_qt4 class ScrollBarTest(unittest.TestCase): def setUp(self): + if NativeScrollBar is None: + raise unittest.SkipTest("Qt4 NativeScrollbar not available.") self.container = Container(position=[0, 0], bounds=[600, 600]) self.window = Window(None, size=(600, 600), component=self.container) From a1afe917ef5be64b5017d361c03e012de31add55 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 30 Aug 2015 11:04:02 +0100 Subject: [PATCH 6/9] Remove unused arguments. --- enable/tests/qt4/scrollbar_test.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/enable/tests/qt4/scrollbar_test.py b/enable/tests/qt4/scrollbar_test.py index 1bff0fe70..40c93a3af 100644 --- a/enable/tests/qt4/scrollbar_test.py +++ b/enable/tests/qt4/scrollbar_test.py @@ -31,7 +31,7 @@ def setup_window(self, window): window.control.destroy() @contextmanager - def setup_scrollbar(self, scrollbar, window, enable_range, value): + def setup_scrollbar(self, scrollbar, window): scrollbar._draw_mainlayer(self, window._gc) try: yield @@ -50,7 +50,7 @@ def test_scroll_position_horizontal(self): ) self.container.add(scrollbar) with self.setup_window(self.window): - with self.setup_scrollbar(scrollbar, self.window, range, 0): + with self.setup_scrollbar(scrollbar, self.window): self.assertEqual(scrollbar._control.value(), 0) self.assertEqual(scrollbar.scroll_position, 0) @@ -75,7 +75,7 @@ def test_scroll_position_vertical(self): ) self.container.add(scrollbar) with self.setup_window(self.window): - with self.setup_scrollbar(scrollbar, self.window, range, 0): + with self.setup_scrollbar(scrollbar, self.window): self.assertEqual(scrollbar._control.value(), 600-375) self.assertEqual(scrollbar.scroll_position, 0) @@ -100,7 +100,7 @@ def test_minumum_horizontal(self): ) self.container.add(scrollbar) with self.setup_window(self.window): - with self.setup_scrollbar(scrollbar, self.window, range, 100): + with self.setup_scrollbar(scrollbar, self.window): self.assertEqual(scrollbar._control.value(), 100) self.assertEqual(scrollbar.scroll_position, 100) @@ -125,7 +125,7 @@ def test_minimum_vertical(self): ) self.container.add(scrollbar) with self.setup_window(self.window): - with self.setup_scrollbar(scrollbar, self.window, range, 100): + with self.setup_scrollbar(scrollbar, self.window): # control should be at top self.assertEqual(scrollbar._control.value(), 700-375) self.assertEqual(scrollbar.scroll_position, 100) From 63c2fa2427902958ae53639cc61a9e401a21e209 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 30 Aug 2015 11:18:03 +0100 Subject: [PATCH 7/9] Process some events after showing for force a paint & hence size. --- enable/tests/qt4/scrollbar_test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/enable/tests/qt4/scrollbar_test.py b/enable/tests/qt4/scrollbar_test.py index 40c93a3af..d15ed117c 100644 --- a/enable/tests/qt4/scrollbar_test.py +++ b/enable/tests/qt4/scrollbar_test.py @@ -3,6 +3,7 @@ from traits.testing.unittest_tools import unittest from traitsui.tests._tools import skip_if_not_qt4 +from pyface.gui import GUI from enable.container import Container from enable.window import Window @@ -19,12 +20,14 @@ class ScrollBarTest(unittest.TestCase): def setUp(self): if NativeScrollBar is None: raise unittest.SkipTest("Qt4 NativeScrollbar not available.") + self.gui = GUI() self.container = Container(position=[0, 0], bounds=[600, 600]) self.window = Window(None, size=(600, 600), component=self.container) @contextmanager def setup_window(self, window): window.control.show() + self.gui.process_events() try: yield finally: From 9a6ca5549eac6067fcde68da6c0e3ccc6d268847 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 4 Sep 2015 16:19:55 +0100 Subject: [PATCH 8/9] Correct signature of _draw_mainlayer --- enable/tests/qt4/scrollbar_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enable/tests/qt4/scrollbar_test.py b/enable/tests/qt4/scrollbar_test.py index d15ed117c..8ffdc0671 100644 --- a/enable/tests/qt4/scrollbar_test.py +++ b/enable/tests/qt4/scrollbar_test.py @@ -35,7 +35,7 @@ def setup_window(self, window): @contextmanager def setup_scrollbar(self, scrollbar, window): - scrollbar._draw_mainlayer(self, window._gc) + scrollbar._draw_mainlayer(window._gc) try: yield finally: From c06524c9829a4c207748fe170093022c678da001 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 4 Sep 2015 16:38:07 +0100 Subject: [PATCH 9/9] Try to force explicit computation of window size. --- enable/tests/qt4/scrollbar_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/enable/tests/qt4/scrollbar_test.py b/enable/tests/qt4/scrollbar_test.py index 8ffdc0671..93600fd58 100644 --- a/enable/tests/qt4/scrollbar_test.py +++ b/enable/tests/qt4/scrollbar_test.py @@ -27,6 +27,7 @@ def setUp(self): @contextmanager def setup_window(self, window): window.control.show() + window._size = window._get_control_size() self.gui.process_events() try: yield