Tue, 27 Jul 2010 08:25:53 +0200
Did some more string format conversions.
# -*- coding: utf-8 -*- # Copyright (c) 2009 - 2010 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing a network monitor dialog. """ from PyQt4.QtGui import * from PyQt4.QtCore import * from PyQt4.QtNetwork import QNetworkRequest, QNetworkAccessManager import UI.PixmapCache from .E5NetworkHeaderDetailsDialog import E5NetworkHeaderDetailsDialog from .Ui_E5NetworkMonitor import Ui_E5NetworkMonitor class E5NetworkRequest(object): """ Class for storing all data related to a specific request. """ def __init__(self): """ Constructor """ self.op = -1 self.request = None self.reply = None self.response = "" self.length = 0 self.contentType = "" self.info = "" self.replyHeaders = [] # list of tuple of two items class E5NetworkMonitor(QDialog, Ui_E5NetworkMonitor): """ Class implementing a network monitor dialog. """ _monitor = None @classmethod def instance(cls, networkAccessManager): """ Class method to get a reference to our singleton. @param networkAccessManager reference to the network access manager (QNetworkAccessManager) """ if cls._monitor is None: cls._monitor = E5NetworkMonitor(networkAccessManager) cls._monitor.setAttribute(Qt.WA_DeleteOnClose, True) return cls._monitor @classmethod def closeMonitor(cls): """ Class method to close the monitor dialog. """ if cls._monitor is not None: cls._monitor.close() def __init__(self, networkAccessManager, parent = None): """ Constructor @param networkAccessManager reference to the network access manager (QNetworkAccessManager) @param parent reference to the parent widget (QWidget) """ QDialog.__init__(self, parent) self.setupUi(self) self.clearButton.setIcon(UI.PixmapCache.getIcon("clearLeft.png")) self.__requestHeaders = QStandardItemModel(self) self.__requestHeaders.setHorizontalHeaderLabels( [self.trUtf8("Name"), self.trUtf8("Value")]) self.requestHeadersList.setModel(self.__requestHeaders) self.requestHeadersList.horizontalHeader().setStretchLastSection(True) self.connect(self.requestHeadersList, SIGNAL("doubleClicked(const QModelIndex&)"), self.__showHeaderDetails) self.__replyHeaders = QStandardItemModel(self) self.__replyHeaders.setHorizontalHeaderLabels( [self.trUtf8("Name"), self.trUtf8("Value")]) self.responseHeadersList.setModel(self.__replyHeaders) self.responseHeadersList.horizontalHeader().setStretchLastSection(True) self.connect(self.responseHeadersList, SIGNAL("doubleClicked(const QModelIndex&)"), self.__showHeaderDetails) self.requestsList.horizontalHeader().setStretchLastSection(True) self.requestsList.verticalHeader().setMinimumSectionSize(-1) self.__proxyModel = QSortFilterProxyModel(self) self.__proxyModel.setFilterKeyColumn(-1) self.connect(self.searchEdit, SIGNAL("textChanged(QString)"), self.__proxyModel.setFilterFixedString) self.connect(self.removeButton, SIGNAL("clicked()"), self.requestsList.removeSelected) self.connect(self.removeAllButton, SIGNAL("clicked()"), self.requestsList.removeAll) self.__model = E5RequestModel(networkAccessManager, self) self.__proxyModel.setSourceModel(self.__model) self.requestsList.setModel(self.__proxyModel) self.connect(self.requestsList.selectionModel(), SIGNAL("currentChanged(const QModelIndex&, const QModelIndex&)"), self.__currentChanged) fm = self.fontMetrics() em = fm.width("m") self.requestsList.horizontalHeader().resizeSection(0, em * 5) self.requestsList.horizontalHeader().resizeSection(1, em * 20) self.requestsList.horizontalHeader().resizeSection(3, em * 5) self.requestsList.horizontalHeader().resizeSection(4, em * 15) self.__headersDlg = None def closeEvent(self, evt): """ Protected method called upon closing the dialog. @param evt reference to the close event object (QCloseEvent) """ self.__class__._monitor = None QDialog.closeEvent(self, evt) def reject(self): """ Public slot to close the dialog with a Reject status. """ self.__class__._monitor = None QDialog.reject(self) def __currentChanged(self, current, previous): """ Private slot to handle a change of the current index. @param current new current index (QModelIndex) @param previous old current index (QModelIndex) """ self.__requestHeaders.setRowCount(0) self.__replyHeaders.setRowCount(0) if not current.isValid(): return row = self.__proxyModel.mapToSource(current).row() req = self.__model.requests[row].request for header in req.rawHeaderList(): self.__requestHeaders.insertRows(0, 1, QModelIndex()) self.__requestHeaders.setData( self.__requestHeaders.index(0, 0), header) self.__requestHeaders.setData( self.__requestHeaders.index(0, 1), req.rawHeader(header)) self.__requestHeaders.item(0, 0).setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.__requestHeaders.item(0, 1).setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled) for header in self.__model.requests[row].replyHeaders: self.__replyHeaders.insertRows(0, 1, QModelIndex()) self.__replyHeaders.setData( self.__replyHeaders.index(0, 0), header[0]) self.__replyHeaders.setData( self.__replyHeaders.index(0, 1), header[1]) self.__replyHeaders.item(0, 0).setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.__replyHeaders.item(0, 1).setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled) def __showHeaderDetails(self, index): """ Private slot to show a dialog with the header details. @param index index of the entry to show (QModelIndex) """ if not index.isValid(): return headerList = self.sender() if headerList is None: return row = index.row() name = headerList.model().data(headerList.model().index(row, 0)) value = headerList.model().data(headerList.model().index(row, 1)) if self.__headersDlg is None: self.__headersDlg = E5NetworkHeaderDetailsDialog(self) self.__headersDlg.setData(name, value) self.__headersDlg.show() class E5RequestModel(QAbstractTableModel): """ Class implementing a model storing request objects. """ def __init__(self, networkAccessManager, parent = None): """ Constructor @param networkAccessManager reference to the network access manager (QNetworkAccessManager) @param parent reference to the parent object (QObject) """ QAbstractTableModel.__init__(self, parent) self.__headerData = [ self.trUtf8("Method"), self.trUtf8("Address"), self.trUtf8("Response"), self.trUtf8("Length"), self.trUtf8("Content Type"), self.trUtf8("Info"), ] self.__operations = { QNetworkAccessManager.HeadOperation : "HEAD", QNetworkAccessManager.GetOperation : "GET", QNetworkAccessManager.PutOperation : "PUT", QNetworkAccessManager.PostOperation : "POST", } self.requests = [] self.connect(networkAccessManager, SIGNAL("requestCreated(QNetworkAccessManager::Operation, const QNetworkRequest&, QNetworkReply*)"), self.__requestCreated) def __requestCreated(self, operation, request, reply): """ Private slot handling the creation of a network request. @param operation network operation (QNetworkAccessManager.Operation) @param request reference to the request object (QNetworkRequest) @param reply reference to the reply object(QNetworkReply) """ req = E5NetworkRequest() req.op = operation req.request = QNetworkRequest(request) req.reply = reply self.__addRequest(req) def __addRequest(self, req): """ Private method to add a request object to the model. @param req reference to the request object (E5NetworkRequest) """ self.beginInsertRows(QModelIndex(), len(self.requests), len(self.requests)) self.requests.append(req) self.connect(req.reply, SIGNAL("finished()"), self.__addReply) self.endInsertRows() def __addReply(self): """ Private slot to add the reply data to the model. """ reply = self.sender() if reply is None: return offset = len(self.requests) - 1 while offset >= 0: if self.requests[offset].reply is reply: break offset -= 1 if offset < 0: return # save the reply header data for header in reply.rawHeaderList(): self.requests[offset].replyHeaders.append((header, reply.rawHeader(header))) # save reply info to be displayed status = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) or 0 reason = reply.attribute(QNetworkRequest.HttpReasonPhraseAttribute) or "" self.requests[offset].response = "{0:d} {1}".format(status, reason) self.requests[offset].length = reply.header(QNetworkRequest.ContentLengthHeader) self.requests[offset].contentType = reply.header(QNetworkRequest.ContentTypeHeader) if status == 302: target = reply.attribute(QNetworkRequest.RedirectionTargetAttribute) or QUrl() self.requests[offset].info = \ self.trUtf8("Redirect: {0}").format(target.toString()) def headerData(self, section, orientation, role = Qt.DisplayRole): """ Public method to get header data from the model. @param section section number (integer) @param orientation orientation (Qt.Orientation) @param role role of the data to retrieve (integer) @return requested data """ if orientation == Qt.Horizontal and role == Qt.DisplayRole: return self.__headerData[section] return QAbstractTableModel.headerData(self, section, orientation, role) def data(self, index, role = Qt.DisplayRole): """ Public method to get data from the model. @param index index to get data for (QModelIndex) @param role role of the data to retrieve (integer) @return requested data """ if index.row() < 0 or index.row() >= len(self.requests): return None if role == Qt.DisplayRole or role == Qt.EditRole: col = index.column() if col == 0: try: return self.__operations[self.requests[index.row()].op] except KeyError: return self.trUtf8("Unknown") elif col == 1: return self.requests[index.row()].request.url().toEncoded() elif col == 2: return self.requests[index.row()].response elif col == 3: return self.requests[index.row()].length elif col == 4: return self.requests[index.row()].contentType elif col == 5: return self.requests[index.row()].info return None def columnCount(self, parent): """ Public method to get the number of columns of the model. @param parent parent index (QModelIndex) @return number of columns (integer) """ if parent.column() > 0: return 0 else: return len(self.__headerData) def rowCount(self, parent): """ Public method to get the number of rows of the model. @param parent parent index (QModelIndex) @return number of columns (integer) """ if parent.isValid(): return 0 else: return len(self.requests) def removeRows(self, row, count, parent): """ Public method to remove entries from the model. @param row start row (integer) @param count number of rows to remove (integer) @param parent parent index (QModelIndex) @return flag indicating success (boolean) """ if parent.isValid(): return False lastRow = row + count - 1 self.beginRemoveRows(parent, row, lastRow) del self.requests[row:lastRow + 1] self.endRemoveRows() return True