Skip to content

Commit

Permalink
Merge pull request #3313 from janezd/improve-updates-disabled
Browse files Browse the repository at this point in the history
utils.updates_disabled: Make it a decorator as well
  • Loading branch information
janezd authored Oct 20, 2018
2 parents 2aa5743 + 9e22424 commit 9b89000
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 9 deletions.
58 changes: 58 additions & 0 deletions Orange/canvas/gui/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import unittest

from AnyQt.QtWidgets import QWidget

from Orange.canvas.gui import utils
from ..test import QAppTestCase


class TestUpdatesDisabled(QAppTestCase):
def test_context_manager(self):
widget = QWidget()
self.assertTrue(widget.updatesEnabled())
with utils.updates_disabled(widget):
self.assertFalse(widget.updatesEnabled())
self.assertTrue(widget.updatesEnabled())

# Disabling twice still does not reenable updates when exiting the
# inner context, but disables afterwards
with utils.updates_disabled(widget):
self.assertFalse(widget.updatesEnabled())
with utils.updates_disabled(widget):
self.assertFalse(widget.updatesEnabled())
self.assertFalse(widget.updatesEnabled())
self.assertTrue(widget.updatesEnabled())

# Updates are reenabled even when exception occurs within the context
with self.assertRaises(ValueError):
with utils.updates_disabled(widget):
self.assertFalse(widget.updatesEnabled())
raise ValueError("foo")
self.assertTrue(widget.updatesEnabled())

def test_decorator(self):
test_self = self

class OWSomeWidget(QWidget):
def __init__(self):
super().__init__()
self.child = QWidget()

@utils.updates_disabled('child')
def method(self, x=1):
test_self.assertFalse(self.child.updatesEnabled())
x = 1 / x
with utils.updates_disabled(self.child):
test_self.assertFalse(self.child.updatesEnabled())
test_self.assertFalse(self.child.updatesEnabled())

widget = OWSomeWidget()
self.assertTrue(widget.child.updatesEnabled())
widget.method()
self.assertTrue(widget.child.updatesEnabled())
self.assertRaises(ZeroDivisionError, widget.method, 0)
self.assertTrue(widget.child.updatesEnabled())


if __name__ == "__main__":
unittest.main()
48 changes: 39 additions & 9 deletions Orange/canvas/gui/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import traceback

from contextlib import contextmanager
from functools import wraps

from AnyQt.QtWidgets import (
QWidget, QMessageBox, QStyleOption, QStyle
Expand All @@ -21,16 +22,45 @@
QWIDGETSIZE_MAX = ((1 << 24) - 1)


@contextmanager
def updates_disabled(widget):
"""Disable QWidget updates (using QWidget.setUpdatesEnabled)
# Serves as a decorator and context manager, hence a function-like name
class updates_disabled: # pylint: disable=invalid-name
"""
old_state = widget.updatesEnabled()
widget.setUpdatesEnabled(False)
try:
yield
finally:
widget.setUpdatesEnabled(old_state)
Disable QWidget updates (using QWidget.setUpdatesEnabled).
`updates_disabled` can be used either as a context manager or a decorator
for a method of a class derived from `QWidget`.
When used as context manager, the argument is an instance of QWidget.
with updates_disabled(self.plot_widget):
do_something()
When used as decorator, the argument is the name of the widget's
attribute containing the child widget whose updates.
@updated_disabled('plot_widget'):
def method(self):
do_something()
In both cases, updates are enabled when the context or the function exits,
and the widget's content is scheduled to be updated.
"""
def __init__(self, widget):
self.widget = widget

def __enter__(self):
self.old_state = self.widget.updatesEnabled()
self.widget.setUpdatesEnabled(False)

def __exit__(self, *exc):
self.widget.setUpdatesEnabled(self.old_state)

def __call__(self, method):
@wraps(method)
def func(widget, *args, **kwargs):
with updates_disabled(getattr(widget, self.widget)):
method(widget, *args, **kwargs)
return func


@contextmanager
Expand Down

0 comments on commit 9b89000

Please sign in to comment.