### Navigation

- index
- modules |
- Camelot documentation »
- Module code »

# Source code for camelot.view.model_thread

    #  ============================================================================
    #
    #  Copyright (C) 2007-2011 Conceptive Engineering bvba. All rights reserved.
    #  www.conceptive.be / project-camelot@conceptive.be
    #
    #  This file is part of the Camelot Library.
    #
    #  This file may be used under the terms of the GNU General Public
    #  License version 2.0 as published by the Free Software Foundation
    #  and appearing in the file license.txt included in the packaging of
    #  this file.  Please review this information to ensure GNU
    #  General Public Licensing requirements will be met.
    #
    #  If you are unsure which license is appropriate for your use, please
    #  visit www.python-camelot.com or contact project-camelot@conceptive.be
    #
    #  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
    #  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    #
    #  For use of this library in commercial applications, please contact
    #  project-camelot@conceptive.be
    #
    #  ============================================================================

    from functools import wraps

    from PyQt4 import QtGui
    from PyQt4 import QtCore

    import logging
    logger = logging.getLogger('camelot.view.model_thread')

    _model_thread_ = []

    # this might be set to False, for unittesting purpose
    verify_threads = True

    class ModelThreadException(Exception):
        pass

    def model_function(original_function):
        """Decorator to ensure a function is only called from within the model
        thread. If this function is called in another thread, an exception will be
        thrown"""

        def in_model_thread():
            """return wether current thread is model thread"""
            from no_thread_model_thread import NoThreadModelThread
            current_thread = QtCore.QThread.currentThread()
            model_thread = get_model_thread()
            return (current_thread==model_thread) or isinstance(
                model_thread, (NoThreadModelThread,)
            )

        @wraps(original_function)
        def wrapper(*args, **kwargs):
            assert (not verify_threads) or in_model_thread()
            return original_function(*args, **kwargs)

        return wrapper

    def gui_function(original_function):
        """Decorator to ensure a function is only called from within the gui
        thread. If this function is called in another thread, an exception will be
        thrown"""

        def in_gui_thread():
            """return wether current thread is gui thread"""
            gui_thread = QtGui.QApplication.instance().thread()
            current_thread = QtCore.QThread.currentThread()
            return gui_thread == current_thread

        @wraps(original_function)
        def wrapper(*args, **kwargs):
            assert (not verify_threads) or in_gui_thread()
            return original_function(*args, **kwargs)

        return wrapper

    def setup_model():
        """Call the setup_model function in the settings"""
        from camelot.core.conf import settings
        settings.setup_model()

    class AbstractModelThread(QtCore.QThread):
        """Abstract implementation of a model thread class
        Thread in which the model runs, all requests to the model should be
        posted to the the model thread.

        This class ensures the gui thread doesn't block when the model needs
        time to complete tasks by providing asynchronous communication between
        the model thread and the gui thread

        The Model thread class provides a number of signals :

        *thread_busy_signal*

        indicates if the model thread is working in the background

        *setup_exception_signal*

        this signal is emitted when there was an exception setting up the model
        thread, eg no connection to the database could be made.  this exception
        is mostly fatal for the application.
        """

        thread_busy_signal = QtCore.pyqtSignal(bool)
        setup_exception_signal = QtCore.pyqtSignal(object)

        def __init__(self, setup_thread=setup_model):
            """:param setup_thread: function to be called at startup of the thread
            to initialize everything, by default this will setup the model. Set to
            None if nothing should be done."""
            super(AbstractModelThread, self).__init__()
            self.logger = logging.getLogger(logger.name + '.%s' % id(self))
            self._setup_thread = setup_thread
            self._exit = False
            self._traceback = ''
            self.logger.debug('model thread constructed')

        def run(self):
            pass

        def traceback(self):
            """The formatted traceback of the last exception in the model thread"""
            return self._traceback

        def wait_on_work(self):
            """Wait for all work to be finished, this function should only be used
        to do unit testing and such, since it will block the calling thread until
        all work is done"""
            pass

        def post(self, request, response=None, exception=None, args=()):
            """Post a request to the model thread, request should be a function
            that takes no arguments. The request function will be called within the
            model thread. When the request is finished, on first occasion, the
            response function will be called within the gui thread. The response
            function takes as arguments, the results of the request function.

            :param request: function to be called within the model thread
            :param response: a slot that will be called with the result of the
            request function
            :param exception: a slot that will be called in case request throws an
            exception
            :param args: arguments with which the request function will be called
            """
            raise NotImplemented

        def busy(self):
            """Return True or False indicating wether either the model or the gui
            thread is doing something"""
            return False

        def stop(self):
            """Stop the model thread from accepting any further posts.
            """
            return True

    def construct_model_thread(*args, **kwargs):
        from signal_slot_model_thread import SignalSlotModelThread
        _model_thread_.insert(0, SignalSlotModelThread(*args, **kwargs))

    def has_model_thread():
        return len(_model_thread_) > 0

    def get_model_thread():
        return _model_thread_[0]

    def post(request, response=None, exception=None, args=()):
        """Post a request and a response to the default model thread"""
        mt = get_model_thread()
        mt.post(request, response, exception, args)

### Navigation

- index
- modules |
- Camelot documentation »
- Module code »

Comments
blog comments powered by Disqus