eric7/Toolbox/SingleApplication.py

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

eric ide

mercurial