/ src / bitmessageqt / networkstatus.py
networkstatus.py
  1  """
  2  Network status tab widget definition.
  3  """
  4  
  5  import time
  6  
  7  from PyQt4 import QtCore, QtGui
  8  
  9  import l10n
 10  import network.stats
 11  import state
 12  from . import widgets
 13  from network import connectionpool, knownnodes
 14  from .retranslateui import RetranslateMixin
 15  from tr import _translate
 16  from .uisignaler import UISignaler
 17  
 18  
 19  class NetworkStatus(QtGui.QWidget, RetranslateMixin):
 20      """Network status tab"""
 21      def __init__(self, parent=None):
 22          super(NetworkStatus, self).__init__(parent)
 23          widgets.load('networkstatus.ui', self)
 24  
 25          header = self.tableWidgetConnectionCount.horizontalHeader()
 26          header.setResizeMode(QtGui.QHeaderView.ResizeToContents)
 27  
 28          # Somehow this value was 5 when I tested
 29          if header.sortIndicatorSection() > 4:
 30              header.setSortIndicator(0, QtCore.Qt.AscendingOrder)
 31  
 32          self.startup = time.localtime()
 33  
 34          self.UISignalThread = UISignaler.get()
 35          # pylint: disable=no-member
 36          QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
 37              "updateNumberOfMessagesProcessed()"), self.updateNumberOfMessagesProcessed)
 38          QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
 39              "updateNumberOfPubkeysProcessed()"), self.updateNumberOfPubkeysProcessed)
 40          QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
 41              "updateNumberOfBroadcastsProcessed()"), self.updateNumberOfBroadcastsProcessed)
 42          QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
 43              "updateNetworkStatusTab(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.updateNetworkStatusTab)
 44  
 45          self.timer = QtCore.QTimer()
 46  
 47          QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds)
 48          # pylint: enable=no-member
 49  
 50      def startUpdate(self):
 51          """Start a timer to update counters every 2 seconds"""
 52          state.Inventory.numberOfInventoryLookupsPerformed = 0
 53          self.runEveryTwoSeconds()
 54          self.timer.start(2000)  # milliseconds
 55  
 56      def stopUpdate(self):
 57          """Stop counter update timer"""
 58          self.timer.stop()
 59  
 60      def formatBytes(self, num):
 61          """Format bytes nicely (SI prefixes)"""
 62          # pylint: disable=no-self-use
 63          for x in [
 64                  _translate(
 65                      "networkstatus",
 66                      "byte(s)",
 67                      None,
 68                      QtCore.QCoreApplication.CodecForTr,
 69                      num),
 70                  "kB",
 71                  "MB",
 72                  "GB",
 73          ]:
 74              if num < 1000.0:
 75                  return "%3.0f %s" % (num, x)
 76              num /= 1000.0
 77          return "%3.0f %s" % (num, 'TB')
 78  
 79      def formatByteRate(self, num):
 80          """Format transfer speed in kB/s"""
 81          # pylint: disable=no-self-use
 82          num /= 1000
 83          return "%4.0f kB" % num
 84  
 85      def updateNumberOfObjectsToBeSynced(self):
 86          """Update the counter for number of objects to be synced"""
 87          self.labelSyncStatus.setText(
 88              _translate(
 89                  "networkstatus",
 90                  "Object(s) to be synced: %n",
 91                  None,
 92                  QtCore.QCoreApplication.CodecForTr,
 93                  network.stats.pendingDownload()
 94                  + network.stats.pendingUpload()))
 95  
 96      def updateNumberOfMessagesProcessed(self):
 97          """Update the counter for number of processed messages"""
 98          self.updateNumberOfObjectsToBeSynced()
 99          self.labelMessageCount.setText(
100              _translate(
101                  "networkstatus",
102                  "Processed %n person-to-person message(s).",
103                  None,
104                  QtCore.QCoreApplication.CodecForTr,
105                  state.numberOfMessagesProcessed))
106  
107      def updateNumberOfBroadcastsProcessed(self):
108          """Update the counter for the number of processed broadcasts"""
109          self.updateNumberOfObjectsToBeSynced()
110          self.labelBroadcastCount.setText(
111              _translate(
112                  "networkstatus",
113                  "Processed %n broadcast message(s).",
114                  None,
115                  QtCore.QCoreApplication.CodecForTr,
116                  state.numberOfBroadcastsProcessed))
117  
118      def updateNumberOfPubkeysProcessed(self):
119          """Update the counter for the number of processed pubkeys"""
120          self.updateNumberOfObjectsToBeSynced()
121          self.labelPubkeyCount.setText(
122              _translate(
123                  "networkstatus",
124                  "Processed %n public key(s).",
125                  None,
126                  QtCore.QCoreApplication.CodecForTr,
127                  state.numberOfPubkeysProcessed))
128  
129      def updateNumberOfBytes(self):
130          """
131          This function is run every two seconds, so we divide the rate of bytes
132          sent and received by 2.
133          """
134          self.labelBytesRecvCount.setText(
135              _translate(
136                  "networkstatus",
137                  "Down: %1/s  Total: %2").arg(
138                      self.formatByteRate(network.stats.downloadSpeed()),
139                      self.formatBytes(network.stats.receivedBytes())))
140          self.labelBytesSentCount.setText(
141              _translate(
142                  "networkstatus", "Up: %1/s  Total: %2").arg(
143                      self.formatByteRate(network.stats.uploadSpeed()),
144                      self.formatBytes(network.stats.sentBytes())))
145  
146      def updateNetworkStatusTab(self, outbound, add, destination):
147          """Add or remove an entry to the list of connected peers"""
148          # pylint: disable=too-many-branches,undefined-variable
149          if outbound:
150              try:
151                  c = connectionpool.pool.outboundConnections[destination]
152              except KeyError:
153                  if add:
154                      return
155          else:
156              try:
157                  c = connectionpool.pool.inboundConnections[destination]
158              except KeyError:
159                  try:
160                      c = connectionpool.pool.inboundConnections[destination.host]
161                  except KeyError:
162                      if add:
163                          return
164  
165          self.tableWidgetConnectionCount.setUpdatesEnabled(False)
166          self.tableWidgetConnectionCount.setSortingEnabled(False)
167  
168          if add:
169              self.tableWidgetConnectionCount.insertRow(0)
170              self.tableWidgetConnectionCount.setItem(
171                  0, 0,
172                  QtGui.QTableWidgetItem("%s:%i" % (destination.host, destination.port))
173              )
174              self.tableWidgetConnectionCount.setItem(
175                  0, 2,
176                  QtGui.QTableWidgetItem("%s" % (c.userAgent))
177              )
178              self.tableWidgetConnectionCount.setItem(
179                  0, 3,
180                  QtGui.QTableWidgetItem("%s" % (c.tlsVersion))
181              )
182              self.tableWidgetConnectionCount.setItem(
183                  0, 4,
184                  QtGui.QTableWidgetItem("%s" % (",".join(map(str, c.streams))))
185              )
186              try:
187                  # .. todo:: FIXME: hard coded stream no
188                  rating = "%.1f" % (knownnodes.knownNodes[1][destination]['rating'])
189              except KeyError:
190                  rating = "-"
191              self.tableWidgetConnectionCount.setItem(
192                  0, 1,
193                  QtGui.QTableWidgetItem("%s" % (rating))
194              )
195              brush = QtGui.QBrush(
196                  QtGui.QColor("yellow" if outbound else "green"),
197                  QtCore.Qt.SolidPattern)
198              for j in range(1):
199                  self.tableWidgetConnectionCount.item(0, j).setBackground(brush)
200                  self.tableWidgetConnectionCount.item(0, j).setForeground(
201                      QtGui.QBrush(QtGui.QColor("black"), QtCore.Qt.SolidPattern))
202              self.tableWidgetConnectionCount.item(0, 0).setData(QtCore.Qt.UserRole, destination)
203              self.tableWidgetConnectionCount.item(0, 1).setData(QtCore.Qt.UserRole, outbound)
204          else:
205              if not connectionpool.pool.inboundConnections:
206                  self.window().setStatusIcon('yellow')
207              for i in range(self.tableWidgetConnectionCount.rowCount()):
208                  if self.tableWidgetConnectionCount.item(i, 0).data(QtCore.Qt.UserRole).toPyObject() != destination:
209                      continue
210                  if self.tableWidgetConnectionCount.item(i, 1).data(QtCore.Qt.UserRole).toPyObject() == outbound:
211                      self.tableWidgetConnectionCount.removeRow(i)
212                      break
213  
214          self.tableWidgetConnectionCount.setUpdatesEnabled(True)
215          self.tableWidgetConnectionCount.setSortingEnabled(True)
216          self.labelTotalConnections.setText(
217              _translate(
218                  "networkstatus", "Total Connections: %1").arg(
219                      str(self.tableWidgetConnectionCount.rowCount())))
220          # FYI: The 'singlelistener' thread sets the icon color to green when it
221          # receives an incoming connection, meaning that the user's firewall is
222          # configured correctly.
223          if self.tableWidgetConnectionCount.rowCount() and state.statusIconColor == 'red':
224              self.window().setStatusIcon('yellow')
225          elif self.tableWidgetConnectionCount.rowCount() == 0 and state.statusIconColor != "red":
226              self.window().setStatusIcon('red')
227  
228      # timer driven
229      def runEveryTwoSeconds(self):
230          """Updates counters, runs every 2 seconds if the timer is running"""
231          self.labelLookupsPerSecond.setText(_translate("networkstatus", "Inventory lookups per second: %1").arg(
232              str(state.Inventory.numberOfInventoryLookupsPerformed / 2)))
233          state.Inventory.numberOfInventoryLookupsPerformed = 0
234          self.updateNumberOfBytes()
235          self.updateNumberOfObjectsToBeSynced()
236  
237      def retranslateUi(self):
238          """Conventional Qt Designer method for dynamic l10n"""
239          super(NetworkStatus, self).retranslateUi()
240          self.labelTotalConnections.setText(
241              _translate(
242                  "networkstatus", "Total Connections: %1").arg(
243                      str(self.tableWidgetConnectionCount.rowCount())))
244          self.labelStartupTime.setText(_translate(
245              "networkstatus", "Since startup on %1"
246          ).arg(l10n.formatTimestamp(self.startup)))
247          self.updateNumberOfMessagesProcessed()
248          self.updateNumberOfBroadcastsProcessed()
249          self.updateNumberOfPubkeysProcessed()