gui.py 3.59 KB
Newer Older
1
2
3
4
5
6
7
8
9
from wsgiref.simple_server import make_server
# from ws4py.websocket import EchoWebSocket
from agkyra.protocol import WebSocketProtocol
from ws4py.server.wsgirefserver import WSGIServer, WebSocketWSGIRequestHandler
from ws4py.server.wsgiutils import WebSocketWSGIApplication
from ws4py.client import WebSocketBaseClient
from tempfile import NamedTemporaryFile
import subprocess
import json
10
from os.path import abspath, join
11
12
13
14
15
from threading import Thread
from hashlib import sha1
import os
import logging

16
CURPATH = os.path.dirname(os.path.abspath(__file__))
17
18
19
20
21
22
23

LOG = logging.getLogger(__name__)


class GUI(WebSocketBaseClient):
    """Launch the GUI when the helper server is ready"""

24
    def __init__(self, addr, gui_id):
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
        """Initialize the GUI Launcher"""
        super(GUI, self).__init__(addr)
        self.addr = addr
        self.gui_id = gui_id
        self.start = self.connect

    def run_gui(self):
        """Launch the GUI and keep it running, clean up afterwards.
        If the GUI is terminated for some reason, the WebSocket is closed and
        the temporary file with GUI settings is deleted.
        In windows, the file must be closed before the GUI is launched.
        """
        # NamedTemporaryFile creates a file accessible only to current user
        LOG.debug('Create temporary file')
        with NamedTemporaryFile(delete=False) as fp:
            json.dump(dict(gui_id=self.gui_id, address=self.addr), fp)
        # subprocess.call blocks the execution
42
        LOG.debug('RUN: %s' % (fp.name))
43
        subprocess.call([
44
45
            os.path.join(os.path.join(CURPATH, 'nwjs'), 'nw'),
            os.path.join(CURPATH, 'gui.nw'),
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
            fp.name,
            '--data-path', abspath('~/.agkyra')])
        LOG.debug('GUI process closed, remove temp file')
        os.remove(fp.name)

    def handshake_ok(self):
        """If handshake is OK, the helper is UP, so the GUI can be launched"""
        self.run_gui()
        LOG.debug('Close GUI wrapper connection')
        self.close()


class HelperServer(object):
    """Agkyra Helper Server sets a WebSocket server with the Helper protocol
    It also provided methods for running and killing the Helper server
    :param gui_id: Only the GUI with this ID is allowed to chat with the Helper
    """

    def __init__(self, port=0):
        """Setup the helper server"""
        self.gui_id = sha1(os.urandom(128)).hexdigest()
        WebSocketProtocol.gui_id = self.gui_id
        server = make_server(
            '', port,
            server_class=WSGIServer,
            handler_class=WebSocketWSGIRequestHandler,
            app=WebSocketWSGIApplication(handler_cls=WebSocketProtocol))
        server.initialize_websockets_manager()
        self.server, self.port = server, server.server_port

    def start(self):
        """Start the helper server in a thread"""
        Thread(target=self.server.serve_forever).start()

    def shutdown(self):
        """Shutdown the server (needs another thread) and join threads"""
        t = Thread(target=self.server.shutdown)
        t.start()
        t.join()


87
def run():
88
89
90
    """Prepare helper and GUI and run them in the proper order"""
    server = HelperServer()
    addr = 'ws://localhost:%s' % server.port
91
    gui = GUI(addr, server.gui_id)
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

    LOG.info('Start helper server')
    server.start()

    try:
        LOG.info('Start GUI')
        gui.start()
    except KeyboardInterrupt:
        LOG.info('Shutdown GUI')
        gui.close()
    LOG.info('Shutdown helper server')
    server.shutdown()

if __name__ == '__main__':
    logging.basicConfig(filename='agkyra.log', level=logging.DEBUG)
    run(abspath('gui/app'))