E4Network/E4NetworkMonitor.py

changeset 0
de9c2efb9d02
child 7
c679fb30c8f3
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 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 E4NetworkHeaderDetailsDialog import E4NetworkHeaderDetailsDialog
17
18 from Ui_E4NetworkMonitor import Ui_E4NetworkMonitor
19
20 class E4NetworkRequest(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 E4NetworkMonitor(QDialog, Ui_E4NetworkMonitor):
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 = E4NetworkMonitor(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 = E4RequestModel(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 QVariant(header))
165 self.__requestHeaders.setData(
166 self.__requestHeaders.index(0, 1),
167 QVariant(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 QVariant(header[0]))
178 self.__replyHeaders.setData(
179 self.__replyHeaders.index(0, 1),
180 QVariant(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)).toString()
201 value = headerList.model().data(headerList.model().index(row, 1)).toString()
202 if self.__headersDlg is None:
203 self.__headersDlg = E4NetworkHeaderDetailsDialog(self)
204 self.__headersDlg.setData(name, value)
205 self.__headersDlg.show()
206
207 class E4RequestModel(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 = E4NetworkRequest()
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 (E4NetworkRequest)
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).toInt()[0]
289 reason = reply.attribute(QNetworkRequest.HttpReasonPhraseAttribute).toString()
290 self.requests[offset].response = "%d %s" % (status, reason)
291 self.requests[offset].length = \
292 reply.header(QNetworkRequest.ContentLengthHeader).toInt()[0]
293 self.requests[offset].contentType = \
294 reply.header(QNetworkRequest.ContentTypeHeader).toString()
295
296 if status == 302:
297 target = reply.attribute(QNetworkRequest.RedirectionTargetAttribute).toUrl()
298 self.requests[offset].info = \
299 self.trUtf8("Redirect: {0}").format(target.toString())
300
301 def headerData(self, section, orientation, role):
302 """
303 Public method to get header data from the model.
304
305 @param section section number (integer)
306 @param orientation orientation (Qt.Orientation)
307 @param role role of the data to retrieve (integer)
308 @return requested data
309 """
310 if orientation == Qt.Horizontal and role == Qt.DisplayRole:
311 return QVariant(self.__headerData[section])
312
313 return QAbstractTableModel.headerData(self, section, orientation, role)
314
315 def data(self, index, role):
316 """
317 Public method to get data from the model.
318
319 @param index index to get data for (QModelIndex)
320 @param role role of the data to retrieve (integer)
321 @return requested data
322 """
323 if index.row() < 0 or index.row() >= len(self.requests):
324 return QVariant()
325
326 if role == Qt.DisplayRole or role == Qt.EditRole:
327 col = index.column()
328 if col == 0:
329 try:
330 return QVariant(self.__operations[self.requests[index.row()].op])
331 except KeyError:
332 return QVariant(self.trUtf8("Unknown"))
333 elif col == 1:
334 return QVariant(self.requests[index.row()].request.url().toEncoded())
335 elif col == 2:
336 return QVariant(self.requests[index.row()].response)
337 elif col == 3:
338 return QVariant(self.requests[index.row()].length)
339 elif col == 4:
340 return QVariant(self.requests[index.row()].contentType)
341 elif col == 5:
342 return QVariant(self.requests[index.row()].info)
343
344 return QVariant()
345
346 def columnCount(self, parent):
347 """
348 Public method to get the number of columns of the model.
349
350 @param parent parent index (QModelIndex)
351 @return number of columns (integer)
352 """
353 if parent.column() > 0:
354 return 0
355 else:
356 return len(self.__headerData)
357
358 def rowCount(self, parent):
359 """
360 Public method to get the number of rows of the model.
361
362 @param parent parent index (QModelIndex)
363 @return number of columns (integer)
364 """
365 if parent.isValid():
366 return 0
367 else:
368 return len(self.requests)
369
370 def removeRows(self, row, count, parent):
371 """
372 Public method to remove entries from the model.
373
374 @param row start row (integer)
375 @param count number of rows to remove (integer)
376 @param parent parent index (QModelIndex)
377 @return flag indicating success (boolean)
378 """
379 if parent.isValid():
380 return False
381
382 lastRow = row + count - 1
383 self.beginRemoveRows(parent, row, lastRow)
384 del self.requests[row:lastRow + 1]
385 self.endRemoveRows()
386 return True

eric ide

mercurial