Toolbox/SingleApplication.py

branch
maintenance
changeset 6646
51eefa621de4
parent 6645
ad476851d7e0
equal deleted inserted replaced
6603:77189681b787 6646:51eefa621de4
1 # -*- coding: utf-8 -*- 1 # -*- coding: utf-8 -*-
2 2
3 # Copyright (c) 2004 - 2018 Detlev Offenbach <detlev@die-offenbachs.de> 3 # Copyright (c) 2004 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 # 4 #
5 5
6 """ 6 """
7 Module implementing the single application server and client. 7 Module implementing the single application server and client.
8 """ 8 """
9 9
10 from __future__ import unicode_literals 10 from __future__ import unicode_literals
11 11
12 import json
13
12 from PyQt5.QtCore import QByteArray 14 from PyQt5.QtCore import QByteArray
13 from PyQt5.QtNetwork import QLocalServer, QLocalSocket 15 from PyQt5.QtNetwork import QLocalServer, QLocalSocket
16
17 from E5Gui import E5MessageBox
18
19 import Utilities
14 20
15 21
16 class SingleApplicationServer(QLocalServer): 22 class SingleApplicationServer(QLocalServer):
17 """ 23 """
18 Class implementing the single application server base class. 24 Class implementing the single application server base class.
39 """ 45 """
40 Private slot to handle a new connection. 46 Private slot to handle a new connection.
41 """ 47 """
42 sock = self.nextPendingConnection() 48 sock = self.nextPendingConnection()
43 49
44 # If we already have a connection, refuse this one. It will be closed 50 # If we already have a connection, refuse this one. It will be closed
45 # automatically. 51 # automatically.
46 if self.qsock is not None: 52 if self.qsock is not None:
47 return 53 return
48 54
49 self.qsock = sock 55 self.qsock = sock
50 56
51 self.qsock.readyRead.connect(self.__parseLine) 57 self.qsock.readyRead.connect(self.__receiveJson)
52 self.qsock.disconnected.connect(self.__disconnected) 58 self.qsock.disconnected.connect(self.__disconnected)
53 59
54 def __parseLine(self): 60 def __receiveJson(self):
55 """ 61 """
56 Private method to handle data from the client. 62 Private method to receive the data from the client.
57 """ 63 """
58 while self.qsock and self.qsock.canReadLine(): 64 while self.qsock and self.qsock.canReadLine():
59 line = bytes(self.qsock.readLine()).decode() 65 line = bytes(self.qsock.readLine()).decode()
60 66
61 ## print(line) ##debug 67 ## print(line) ##debug
62 68
63 eoc = line.find('<') + 1 69 try:
64 70 commandDict = json.loads(line.strip())
65 boc = line.find('>') 71 except (TypeError, ValueError) as err:
66 if boc >= 0 and eoc > boc: 72 E5MessageBox.critical(
67 # handle the command sent by the client. 73 None,
68 cmd = line[boc:eoc] 74 self.tr("Single Application Protocol Error"),
69 params = line[eoc:-1] 75 self.tr("""<p>The response received from the single"""
70 76 """ application client could not be decoded."""
71 self.handleCommand(cmd, params) 77 """ Please report this issue with the received"""
78 """ data to the eric bugs email address.</p>"""
79 """<p>Error: {0}</p>"""
80 """<p>Data:<br/>{1}</p>""").format(
81 str(err), Utilities.html_encode(line.strip())),
82 E5MessageBox.StandardButtons(
83 E5MessageBox.Ok))
84 return
85
86 command = commandDict["command"]
87 arguments = commandDict["arguments"]
88
89 self.handleCommand(command, arguments)
72 90
73 def __disconnected(self): 91 def __disconnected(self):
74 """ 92 """
75 Private method to handle the closure of the socket. 93 Private method to handle the closure of the socket.
76 """ 94 """
86 104
87 self.qsock = None 105 self.qsock = None
88 106
89 self.close() 107 self.close()
90 108
91 def handleCommand(self, cmd, params): 109 def handleCommand(self, command, arguments):
92 """ 110 """
93 Public slot to handle the command sent by the client. 111 Public slot to handle the command sent by the client.
94 112
95 <b>Note</b>: This method must be overridden by subclasses. 113 <b>Note</b>: This method must be overridden by subclasses.
96 114
97 @param cmd commandstring (string) 115 @param command command sent by the client
98 @param params parameterstring (string) 116 @type str
117 @param arguments list of command arguments
118 @type list of str
99 @exception RuntimeError raised to indicate that this method must be 119 @exception RuntimeError raised to indicate that this method must be
100 implemented by a subclass 120 implemented by a subclass
101 """ 121 """
102 raise RuntimeError("'handleCommand' must be overridden") 122 raise RuntimeError("'handleCommand' must be overridden")
103 123
113 @param name name of the local server to connect to (string) 133 @param name name of the local server to connect to (string)
114 """ 134 """
115 self.name = name 135 self.name = name
116 self.connected = False 136 self.connected = False
117 137
118 def connect(self): 138 def connect(self, timeout=10000):
119 """ 139 """
120 Public method to connect the single application client to its server. 140 Public method to connect the single application client to its server.
121 141
142 @param timeout connection timeout value in milliseconds
143 @type int
122 @return value indicating success or an error number. Value is one of: 144 @return value indicating success or an error number. Value is one of:
123 <table> 145 <table>
124 <tr><td>0</td><td>No application is running</td></tr> 146 <tr><td>0</td><td>No application is running</td></tr>
125 <tr><td>1</td><td>Application is already running</td></tr> 147 <tr><td>1</td><td>Application is already running</td></tr>
126 </table> 148 </table>
127 """ 149 """
128 self.sock = QLocalSocket() 150 self.sock = QLocalSocket()
129 self.sock.connectToServer(self.name) 151 self.sock.connectToServer(self.name)
130 if self.sock.waitForConnected(10000): 152 if self.sock.waitForConnected(timeout):
131 self.connected = True 153 self.connected = True
132 return 1 154 return 1
133 else: 155 else:
134 err = self.sock.error() 156 err = self.sock.error()
135 if err == QLocalSocket.ServerNotFoundError: 157 if err == QLocalSocket.ServerNotFoundError:
154 @exception RuntimeError raised to indicate that this method must be 176 @exception RuntimeError raised to indicate that this method must be
155 implemented by a subclass 177 implemented by a subclass
156 """ 178 """
157 raise RuntimeError("'processArgs' must be overridden") 179 raise RuntimeError("'processArgs' must be overridden")
158 180
159 def sendCommand(self, cmd): 181 def sendCommand(self, command, arguments):
160 """ 182 """
161 Public method to send the command to the application server. 183 Public method to send the command to the application server.
162 184
163 @param cmd command to be sent (string) 185 @param command command to be sent to the server
186 @type str
187 @param arguments list of command arguments
188 @type list of str
164 """ 189 """
165 if self.connected: 190 if self.connected:
166 self.sock.write(QByteArray(cmd.encode())) 191 commandDict = {
192 "command": command,
193 "arguments": arguments,
194 }
195 self.sock.write(QByteArray(
196 "{0}\n".format(json.dumps(commandDict)).encode()
197 ))
167 self.sock.flush() 198 self.sock.flush()
168 199
169 def errstr(self): 200 def errstr(self):
170 """ 201 """
171 Public method to return a meaningful error string for the last error. 202 Public method to return a meaningful error string for the last error.

eric ide

mercurial