E5Network/E5NetworkMonitor.py

changeset 52
ba69827929ee
child 168
3383117f894b
equal deleted inserted replaced
51:7d80b0f20ca6 52:ba69827929ee
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2010 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a network monitor dialog.
8 """
9
10 from PyQt4.QtGui import *
11 from PyQt4.QtCore import *
12 from PyQt4.QtNetwork import QNetworkRequest, QNetworkAccessManager
13
14 import UI.PixmapCache
15
16 from .E5NetworkHeaderDetailsDialog import E5NetworkHeaderDetailsDialog
17
18 from .Ui_E5NetworkMonitor import Ui_E5NetworkMonitor
19
20 class E5NetworkRequest(object):
21 """
22 Class for storing all data related to a specific request.
23 """
24 def __init__(self):
25 """
26 Constructor
27 """
28 self.op = -1
29 self.request = None
30 self.reply = None
31
32 self.response = ""
33 self.length = 0
34 self.contentType = ""
35 self.info = ""
36 self.replyHeaders = [] # list of tuple of two items
37
38 class E5NetworkMonitor(QDialog, Ui_E5NetworkMonitor):
39 """
40 Class implementing a network monitor dialog.
41 """
42 _monitor = None
43
44 @classmethod
45 def instance(cls, networkAccessManager):
46 """
47 Class method to get a reference to our singleton.
48
49 @param networkAccessManager reference to the network access manager
50 (QNetworkAccessManager)
51 """
52 if cls._monitor is None:
53 cls._monitor = E5NetworkMonitor(networkAccessManager)
54 cls._monitor.setAttribute(Qt.WA_DeleteOnClose, True)
55
56 return cls._monitor
57
58 @classmethod
59 def closeMonitor(cls):
60 """
61 Class method to close the monitor dialog.
62 """
63 if cls._monitor is not None:
64 cls._monitor.close()
65
66 def __init__(self, networkAccessManager, parent = None):
67 """
68 Constructor
69
70 @param networkAccessManager reference to the network access manager
71 (QNetworkAccessManager)
72 @param parent reference to the parent widget (QWidget)
73 """
74 QDialog.__init__(self, parent)
75 self.setupUi(self)
76
77 self.clearButton.setIcon(UI.PixmapCache.getIcon("clearLeft.png"))
78
79 self.__requestHeaders = QStandardItemModel(self)
80 self.__requestHeaders.setHorizontalHeaderLabels(
81 [self.trUtf8("Name"), self.trUtf8("Value")])
82 self.requestHeadersList.setModel(self.__requestHeaders)
83 self.requestHeadersList.horizontalHeader().setStretchLastSection(True)
84 self.connect(self.requestHeadersList,
85 SIGNAL("doubleClicked(const QModelIndex&)"),
86 self.__showHeaderDetails)
87
88 self.__replyHeaders = QStandardItemModel(self)
89 self.__replyHeaders.setHorizontalHeaderLabels(
90 [self.trUtf8("Name"), self.trUtf8("Value")])
91 self.responseHeadersList.setModel(self.__replyHeaders)
92 self.responseHeadersList.horizontalHeader().setStretchLastSection(True)
93 self.connect(self.responseHeadersList,
94 SIGNAL("doubleClicked(const QModelIndex&)"),
95 self.__showHeaderDetails)
96
97 self.requestsList.horizontalHeader().setStretchLastSection(True)
98 self.requestsList.verticalHeader().setMinimumSectionSize(-1)
99
100 self.__proxyModel = QSortFilterProxyModel(self)
101 self.__proxyModel.setFilterKeyColumn(-1)
102 self.connect(self.searchEdit, SIGNAL("textChanged(QString)"),
103 self.__proxyModel.setFilterFixedString)
104
105 self.connect(self.removeButton, SIGNAL("clicked()"),
106 self.requestsList.removeSelected)
107 self.connect(self.removeAllButton, SIGNAL("clicked()"),
108 self.requestsList.removeAll)
109
110 self.__model = E5RequestModel(networkAccessManager, self)
111 self.__proxyModel.setSourceModel(self.__model)
112 self.requestsList.setModel(self.__proxyModel)
113 self.connect(self.requestsList.selectionModel(),
114 SIGNAL("currentChanged(const QModelIndex&, const QModelIndex&)"),
115 self.__currentChanged)
116
117 fm = self.fontMetrics()
118 em = fm.width("m")
119 self.requestsList.horizontalHeader().resizeSection(0, em * 5)
120 self.requestsList.horizontalHeader().resizeSection(1, em * 20)
121 self.requestsList.horizontalHeader().resizeSection(3, em * 5)
122 self.requestsList.horizontalHeader().resizeSection(4, em * 15)
123
124 self.__headersDlg = None
125
126 def closeEvent(self, evt):
127 """
128 Protected method called upon closing the dialog.
129
130 @param evt reference to the close event object (QCloseEvent)
131 """
132 self.deleteLater()
133 self.__class__._monitor = None
134 QDialog.closeEvent(self, evt)
135
136 def reject(self):
137 """
138 Public slot to close the dialog with a Reject status.
139 """
140 self.__class__._monitor = None
141 QDialog.reject(self)
142
143 def __currentChanged(self, current, previous):
144 """
145 Private slot to handle a change of the current index.
146
147 @param current new current index (QModelIndex)
148 @param previous old current index (QModelIndex)
149 """
150 self.__requestHeaders.setRowCount(0)
151 self.__replyHeaders.setRowCount(0)
152
153 if not current.isValid():
154 return
155
156 row = self.__proxyModel.mapToSource(current).row()
157
158 req = self.__model.requests[row].request
159
160 for header in req.rawHeaderList():
161 self.__requestHeaders.insertRows(0, 1, QModelIndex())
162 self.__requestHeaders.setData(
163 self.__requestHeaders.index(0, 0),
164 header)
165 self.__requestHeaders.setData(
166 self.__requestHeaders.index(0, 1),
167 req.rawHeader(header))
168 self.__requestHeaders.item(0, 0).setFlags(
169 Qt.ItemIsSelectable | Qt.ItemIsEnabled)
170 self.__requestHeaders.item(0, 1).setFlags(
171 Qt.ItemIsSelectable | Qt.ItemIsEnabled)
172
173 for header in self.__model.requests[row].replyHeaders:
174 self.__replyHeaders.insertRows(0, 1, QModelIndex())
175 self.__replyHeaders.setData(
176 self.__replyHeaders.index(0, 0),
177 header[0])
178 self.__replyHeaders.setData(
179 self.__replyHeaders.index(0, 1),
180 header[1])
181 self.__replyHeaders.item(0, 0).setFlags(
182 Qt.ItemIsSelectable | Qt.ItemIsEnabled)
183 self.__replyHeaders.item(0, 1).setFlags(
184 Qt.ItemIsSelectable | Qt.ItemIsEnabled)
185
186 def __showHeaderDetails(self, index):
187 """
188 Private slot to show a dialog with the header details.
189
190 @param index index of the entry to show (QModelIndex)
191 """
192 if not index.isValid():
193 return
194
195 headerList = self.sender()
196 if headerList is None:
197 return
198
199 row = index.row()
200 name = headerList.model().data(headerList.model().index(row, 0))
201 value = headerList.model().data(headerList.model().index(row, 1))
202 if self.__headersDlg is None:
203 self.__headersDlg = E5NetworkHeaderDetailsDialog(self)
204 self.__headersDlg.setData(name, value)
205 self.__headersDlg.show()
206
207 class E5RequestModel(QAbstractTableModel):
208 """
209 Class implementing a model storing request objects.
210 """
211 def __init__(self, networkAccessManager, parent = None):
212 """
213 Constructor
214
215 @param networkAccessManager reference to the network access manager
216 (QNetworkAccessManager)
217 @param parent reference to the parent object (QObject)
218 """
219 QAbstractTableModel.__init__(self, parent)
220
221 self.__headerData = [
222 self.trUtf8("Method"),
223 self.trUtf8("Address"),
224 self.trUtf8("Response"),
225 self.trUtf8("Length"),
226 self.trUtf8("Content Type"),
227 self.trUtf8("Info"),
228 ]
229
230 self.__operations = {
231 QNetworkAccessManager.HeadOperation : "HEAD",
232 QNetworkAccessManager.GetOperation : "GET",
233 QNetworkAccessManager.PutOperation : "PUT",
234 QNetworkAccessManager.PostOperation : "POST",
235 }
236
237 self.requests = []
238 self.connect(networkAccessManager,
239 SIGNAL("requestCreated(QNetworkAccessManager::Operation, const QNetworkRequest&, QNetworkReply*)"),
240 self.__requestCreated)
241
242 def __requestCreated(self, operation, request, reply):
243 """
244 Private slot handling the creation of a network request.
245
246 @param operation network operation (QNetworkAccessManager.Operation)
247 @param request reference to the request object (QNetworkRequest)
248 @param reply reference to the reply object(QNetworkReply)
249 """
250 req = E5NetworkRequest()
251 req.op = operation
252 req.request = QNetworkRequest(request)
253 req.reply = reply
254 self.__addRequest(req)
255
256 def __addRequest(self, req):
257 """
258 Private method to add a request object to the model.
259
260 @param req reference to the request object (E5NetworkRequest)
261 """
262 self.beginInsertRows(QModelIndex(), len(self.requests), len(self.requests))
263 self.requests.append(req)
264 self.connect(req.reply, SIGNAL("finished()"), self.__addReply)
265 self.endInsertRows()
266
267 def __addReply(self):
268 """
269 Private slot to add the reply data to the model.
270 """
271 reply = self.sender()
272 if reply is None:
273 return
274
275 offset = len(self.requests) - 1
276 while offset >= 0:
277 if self.requests[offset].reply is reply:
278 break
279 offset -= 1
280 if offset < 0:
281 return
282
283 # save the reply header data
284 for header in reply.rawHeaderList():
285 self.requests[offset].replyHeaders.append((header, reply.rawHeader(header)))
286
287 # save reply info to be displayed
288 status = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) or 0
289 reason = reply.attribute(QNetworkRequest.HttpReasonPhraseAttribute) or ""
290 self.requests[offset].response = "%d %s" % (status, reason)
291 self.requests[offset].length = reply.header(QNetworkRequest.ContentLengthHeader)
292 self.requests[offset].contentType = reply.header(QNetworkRequest.ContentTypeHeader)
293
294 if status == 302:
295 target = reply.attribute(QNetworkRequest.RedirectionTargetAttribute) or QUrl()
296 self.requests[offset].info = \
297 self.trUtf8("Redirect: {0}").format(target.toString())
298
299 def headerData(self, section, orientation, role):
300 """
301 Public method to get header data from the model.
302
303 @param section section number (integer)
304 @param orientation orientation (Qt.Orientation)
305 @param role role of the data to retrieve (integer)
306 @return requested data
307 """
308 if orientation == Qt.Horizontal and role == Qt.DisplayRole:
309 return self.__headerData[section]
310
311 return QAbstractTableModel.headerData(self, section, orientation, role)
312
313 def data(self, index, role):
314 """
315 Public method to get data from the model.
316
317 @param index index to get data for (QModelIndex)
318 @param role role of the data to retrieve (integer)
319 @return requested data
320 """
321 if index.row() < 0 or index.row() >= len(self.requests):
322 return None
323
324 if role == Qt.DisplayRole or role == Qt.EditRole:
325 col = index.column()
326 if col == 0:
327 try:
328 return self.__operations[self.requests[index.row()].op]
329 except KeyError:
330 return self.trUtf8("Unknown")
331 elif col == 1:
332 return self.requests[index.row()].request.url().toEncoded()
333 elif col == 2:
334 return self.requests[index.row()].response
335 elif col == 3:
336 return self.requests[index.row()].length
337 elif col == 4:
338 return self.requests[index.row()].contentType
339 elif col == 5:
340 return self.requests[index.row()].info
341
342 return None
343
344 def columnCount(self, parent):
345 """
346 Public method to get the number of columns of the model.
347
348 @param parent parent index (QModelIndex)
349 @return number of columns (integer)
350 """
351 if parent.column() > 0:
352 return 0
353 else:
354 return len(self.__headerData)
355
356 def rowCount(self, parent):
357 """
358 Public method to get the number of rows of the model.
359
360 @param parent parent index (QModelIndex)
361 @return number of columns (integer)
362 """
363 if parent.isValid():
364 return 0
365 else:
366 return len(self.requests)
367
368 def removeRows(self, row, count, parent):
369 """
370 Public method to remove entries from the model.
371
372 @param row start row (integer)
373 @param count number of rows to remove (integer)
374 @param parent parent index (QModelIndex)
375 @return flag indicating success (boolean)
376 """
377 if parent.isValid():
378 return False
379
380 lastRow = row + count - 1
381 self.beginRemoveRows(parent, row, lastRow)
382 del self.requests[row:lastRow + 1]
383 self.endRemoveRows()
384 return True

eric ide

mercurial