1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 import logging
29 from functools import wraps
30
31 from PyQt4 import QtCore, QtGui
32
33 logger = logging.getLogger( 'camelot.view.model_thread' )
34
35 _model_thread_ = []
39
52
53 @wraps(original_function)
54 def wrapper( *args, **kwargs ):
55 assert in_model_thread()
56 return original_function( *args, **kwargs )
57
58 return wrapper
59
61 """Decorator to ensure a function is only called from within the gui thread.
62 If this function is called in another thread, an exception will be thrown
63 """
64
65 def in_gui_thread():
66 """return wether current thread is gui thread"""
67 gui_thread = QtGui.QApplication.instance().thread()
68 current_thread = QtCore.QThread.currentThread()
69 return gui_thread == current_thread
70
71 @wraps( original_function )
72 def wrapper( *args, **kwargs ):
73 assert in_gui_thread()
74 return original_function( *args, **kwargs )
75
76 return wrapper
77
82
84 """Abstract implementation of a model thread class
85 Thread in which the model runs, all requests to the model should be
86 posted to the the model thread.
87
88 This class ensures the gui thread doesn't block when the model needs
89 time to complete tasks by providing asynchronous communication between
90 the model thread and the gui thread
91 """
92
93 thread_busy_signal = QtCore.SIGNAL('thread_busy')
94
96 """
97 @param setup_thread: function to be called at startup of the thread to initialize
98 everything, by default this will setup the model. set to None if nothing should
99 be done.
100 """
101 self.logger = logging.getLogger( logger.name + '.%s' % id( self ) )
102 self._setup_thread = setup_thread
103 self._exit = False
104 self._traceback = ''
105 self.logger.debug( 'model thread constructed' )
106
109
111 """The formatted traceback of the last exception in the model thread"""
112 return self._traceback
113
115 """Wait for all work to be finished, this function should only be used
116 to do unit testing and such, since it will block the calling thread until
117 all work is done"""
118 pass
119
120 - def post( self, request, response = None, exception = None ):
121 """Post a request to the model thread, request should be
122 a function that takes no arguments. The request function
123 will be called within the model thread. When the request
124 is finished, on first occasion, the response function will be
125 called within the gui thread. The response function takes as
126 arguments, the results of the request function.
127 @param request: function to be called within the model thread
128 @param response: a slot that will be called with the result of the
129 request function
130 @param exception: a slot that will be called in case request throws
131 an exception
132 """
133 raise NotImplemented
134
136 """Return True or False indicating wether either the model or the
137 gui thread is doing something"""
138 return False
139
149
152
155
156 -def post(request, response = None, exception = None):
157 """Post a request and a response to the default model thread"""
158 mt = get_model_thread()
159 mt.post(request, response, exception)
160