~garyvdm/+junk/thread-test

1 by Gary van der Merwe
Initial commit.
1
#!/usr/bin/env python
2
3
import sys
4
import threading
5
import time
6
from PyQt4 import QtCore, QtGui
7
8
main_event_loop = None
4 by Gary van der Merwe
Don't send signals if not necessary.
9
main_tread = threading.currentThread()
10
gui_thread = None
11
12
def runs_in_gui_thread(func):
13
    def runs_in_gui_thread_decorate(*args, **kargs):
14
        run_in_thread(gui_thread, gui_thread.app, func, *args, **kargs)
15
    return runs_in_gui_thread_decorate
16
17
def runs_in_main_thread(func):
18
    def runs_in_main_thread_decorate(*args, **kargs):
19
        run_in_thread(main_tread, main_event_loop, func, *args, **kargs)
20
    return runs_in_main_thread_decorate
21
22
def run_in_thread(thread, signal_object, func, *args, **kargs):
23
    current_thread = threading.currentThread()
24
    if not current_thread == thread:
25
        print 'Signal sent from %s to %s to run %s' % (
26
            current_thread.name, thread.name, func.__name__)
27
        signal_object.emit(QtCore.SIGNAL("run_func"), func, args, kargs)
28
    else:
29
        func(*args, **kargs)
30
    
1 by Gary van der Merwe
Initial commit.
31
32
def run_func(func, args, kargs):
33
    func(*args, **kargs)
34
35
class GUIThread(threading.Thread):
36
    app = None
37
    app_created = threading.Event()
38
    main_event_loop = None
39
    
40
    def __init__(self, main_event_loop_created,
41
                 start_func, start_func_args, start_func_kargs,
42
                 *args, **kargs):
43
        threading.Thread.__init__(self, *args, **kargs)
44
        self.main_event_loop_created = main_event_loop_created
45
        self.start_func = start_func
46
        self.start_func_args = start_func_args
47
        self.start_func_kargs = start_func_kargs
48
    
49
    def run(self):
50
        # uncomment to test for a slow QApplication start
51
        #time.sleep(3)
52
        self.app = QtGui.QApplication(sys.argv)
53
        self.app_created.set()
4 by Gary van der Merwe
Don't send signals if not necessary.
54
        self.app.connect(self.app, QtCore.SIGNAL('run_func'), run_func)
1 by Gary van der Merwe
Initial commit.
55
        
56
        main_event_loop_created.wait()
57
        # Not setting the return val for this func results in the window not
58
        # showing. I don't understand why. It is possible to have a list
59
        # of windows work.
60
        win = self.start_func(*self.start_func_args, **self.start_func_kargs)
61
        
62
        ret = self.app.exec_()
63
        self.main_event_loop.exit(ret)
5 by Gary van der Merwe
Fix segfault by deleting app in the gui_thread.
64
        del self.app
1 by Gary van der Merwe
Initial commit.
65
66
def command_run():
67
    print_where()
68
    win = CommandWindow()
69
    win.show()
70
    return win
71
72
def print_where():
73
    func_name = sys._getframe(1).f_code.co_name
74
    thread_name = threading.currentThread().name
75
    print '%s in %s' % (func_name, thread_name)
76
77
78
class CommandWindow(QtGui.QLabel):
79
    def __init__(self, *args, **kargs):
80
        QtGui.QLabel.__init__(self, *args, **kargs)
3 by Gary van der Merwe
Resize.
81
        self.resize(QtCore.QSize(300, 20))
1 by Gary van der Merwe
Initial commit.
82
    
83
    def show(self):
84
        QtGui.QWidget.show(self)
2 by Gary van der Merwe
Don't need timer to run load.
85
        self.load()
1 by Gary van der Merwe
Initial commit.
86
    
4 by Gary van der Merwe
Don't send signals if not necessary.
87
    @runs_in_main_thread
1 by Gary van der Merwe
Initial commit.
88
    def load(self):
89
        print_where()
90
        self.get_text()
91
    
4 by Gary van der Merwe
Don't send signals if not necessary.
92
    @runs_in_main_thread
1 by Gary van der Merwe
Initial commit.
93
    def get_text(self):
94
        print_where()
95
        self.got_text('Hello World.')
96
    
4 by Gary van der Merwe
Don't send signals if not necessary.
97
    @runs_in_gui_thread
1 by Gary van der Merwe
Initial commit.
98
    def got_text(self, text):
99
        print_where()
100
        self.setText(text)
101
    
102
103
if __name__ == '__main__':
104
    main_event_loop_created = threading.Event()
105
    gui_thread = GUIThread(main_event_loop_created, command_run, [], {}, name='GUIThread')
106
    gui_thread.start()
107
    
108
    # We don't want to create this event loop before the app is created in
109
    # the gui thread.
110
    gui_thread.app_created.wait()
111
    
112
    # this fails because it's not in the GUI Thread
113
    #win = QtGui.QWidget()
114
    #win.show()
115
    
116
    main_event_loop = QtCore.QEventLoop()
117
    main_event_loop.connect(main_event_loop, QtCore.SIGNAL('run_func'), run_func)
118
    main_event_loop_created.set()
119
    
120
    gui_thread.main_event_loop = main_event_loop
121
    ret = main_event_loop.exec_()
122
    gui_thread.join()
123
    sys.exit(ret)
124
125
126