title: twisted.internet.error.ReactorNotRestartable after trying reactor.connectTCP to another IP adress
body: <p>I need to open more than one instances through rdp. For this challenge I use rdpy library and its example <a href="https://github.com/citronneur/rdpy/blob/master/bin/rdpy-rdpscreenshot.py" rel="nofollow noreferrer">https://github.com/citronneur/rdpy/blob/master/bin/rdpy-rdpscreenshot.py</a>. First rdp connect and PyQt window is being opened and closed successfully but after trying to open the second window i' m getting the following error:</p>
  
  <pre><code>Traceback (most recent call last):
    File "C:/Users/R.Halyuta/Downloads/rdpy-master/rdpy-master/bin/rdpy-rdpclient.py", line 265, in &lt;module&gt;
      create_rpds(i["Password"].encode('utf-8'), i["IpAddress"].encode('utf-8'))
    File "C:/Users/R.Halyuta/Downloads/rdpy-master/rdpy-master/bin/rdpy-rdpclient.py", line 225, in create_rpds
      reactor.run()
    File "C:\Python27\lib\site-packages\qtreactor\qt4base.py", line 281, in run
      self.runReturn()
    File "C:\Python27\lib\site-packages\qtreactor\qt4base.py", line 273, in runReturn
      self.startRunning(installSignalHandlers=installSignalHandlers)
    File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 1222, in startRunning
      ReactorBase.startRunning(self)
    File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 730, in startRunning
      raise error.ReactorNotRestartable()
  twisted.internet.error.ReactorNotRestartable
  </code></pre>
  
  <p>Are there any solutions for my issue?
  Here is my code:</p>
  
  <pre><code>import threading
  import time
  import sys, os, getopt, socket
  import json
  from PyQt4 import QtGui, QtCore
  from rdpy.ui.qt4 import RDPClientQt
  from rdpy.protocol.rdp import rdp
  from rdpy.core.error import RDPSecurityNegoFail
  from rdpy.core import rss
  from twisted.internet import error
  import rdpy.core.log as log
  
  log._LOG_LEVEL = log.Level.INFO
  
  
  class RDPClientQtRecorder(RDPClientQt):
      """
      @summary: Widget with record session
      """
  
      def __init__(self, controller, width, height, rssRecorder):
          """
          @param controller: {RDPClientController} RDP controller
          @param width: {int} width of widget
          @param height: {int} height of widget
          @param rssRecorder: {rss.FileRecorder}
          """
          RDPClientQt.__init__(self, controller, width, height)
          self._screensize = width, height
          self._rssRecorder = rssRecorder
  
      def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
          """
          @summary: Notify bitmap update
          @param destLeft: {int} xmin position
          @param destTop: {int} ymin position
          @param destRight: {int} xmax position because RDP can send bitmap with padding
          @param destBottom: {int} ymax position because RDP can send bitmap with padding
          @param width: {int} width of bitmap
          @param height: {int} height of bitmap
          @param bitsPerPixel: {int} number of bit per pixel
          @param isCompress: {bool} use RLE compression
          @param data: {str} bitmap data
          """
          # record update
          self._rssRecorder.update(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel,
                                   rss.UpdateFormat.BMP if isCompress else rss.UpdateFormat.RAW, data)
          RDPClientQt.onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress,
                               data)
  
      def onReady(self):
          """
          @summary: Call when stack is ready
          """
          self._rssRecorder.screen(self._screensize[0], self._screensize[1], self._controller.getColorDepth())
          RDPClientQt.onReady(self)
  
      def onClose(self):
          """
          @summary: Call when stack is close
          """
          self._rssRecorder.close()
          RDPClientQt.onClose(self)
  
      def closeEvent(self, e):
          """
          @summary: Convert Qt close widget event into close stack event
          @param e: QCloseEvent
          """
          self._rssRecorder.close()
          RDPClientQt.closeEvent(self, e)
  
  
  class RDPClientQtFactory(rdp.ClientFactory):
      """
      @summary: Factory create a RDP GUI client
      """
  
      def __init__(self, width, height, username, password, domain, fullscreen, keyboardLayout, optimized, security,
                   recodedPath):
          """
          @param width: {integer} width of client
          @param heigth: {integer} heigth of client
          @param username: {str} username present to the server
          @param password: {str} password present to the server
          @param domain: {str} microsoft domain
          @param fullscreen: {bool} show widget in fullscreen mode
          @param keyboardLayout: {str} (fr|en) keyboard layout
          @param optimized: {bool} enable optimized session orders
          @param security: {str} (ssl | rdp | nego)
          @param recodedPath: {str | None} Rss file Path
          """
          self._width = width
          self._height = height
          self._username = username
          self._passwod = password
          self._domain = domain
          self._fullscreen = fullscreen
          self._keyboardLayout = keyboardLayout
          self._optimized = optimized
          self._nego = security == "nego"
          self._recodedPath = recodedPath
          if self._nego:
              # compute start nego nla need credentials
              if username != "" and password != "":
                  self._security = rdp.SecurityLevel.RDP_LEVEL_NLA
              else:
                  self._security = rdp.SecurityLevel.RDP_LEVEL_SSL
          else:
              self._security = security
          self._w = None
  
      def buildObserver(self, controller, addr):
          """
          @summary:  Build RFB observer
                      We use a RDPClientQt as RDP observer
          @param controller: build factory and needed by observer
          @param addr: destination address
          @return: RDPClientQt
          """
          # create client observer
          if self._recodedPath is None:
              self._client = RDPClientQt(controller, self._width, self._height)
          else:
              self._client = RDPClientQtRecorder(controller, self._width, self._height,
                                                 rss.createRecorder(self._recodedPath))
          # create qt widget
          self._w = self._client.getWidget()
          self._w.setWindowTitle('rdpy-rdpclient')
          if self._fullscreen:
              self._w.showFullScreen()
          else:
              self._w.show()
              self._w.show()
              QtCore.QTimer.singleShot(10000, self._w.close)
          controller.setUsername(self._username)
          controller.setPassword(self._passwod)
          controller.setDomain(self._domain)
          controller.setKeyboardLayout(self._keyboardLayout)
          controller.setHostname(socket.gethostname())
          if self._optimized:
              controller.setPerformanceSession()
          controller.setSecurityLevel(self._security)
  
          return self._client
  
      def clientConnectionLost(self, connector, reason):
          # from twisted.internet import reactor
          """
          @summary: Connection lost event
          @param connector: twisted connector use for rdp connection (use reconnect to restart connection)
          @param reason: str use to advertise reason of lost connection
          """
          # try reconnect with basic RDP security
          if reason.type == RDPSecurityNegoFail and self._nego:
              # stop nego
              log.info("due to security nego error back to standard RDP security layer")
              self._nego = False
              self._security = rdp.SecurityLevel.RDP_LEVEL_RDP
              self._client._widget.hide()
              connector.connect()
              return
          log.info("Lost connection : %s" % reason)
          reactor.stop()
          app.exit()
  
      def clientConnectionFailed(self, connector, reason):
          """
          @summary: Connection failed event
          @param connector: twisted connector use for rdp connection (use reconnect to restart connection)
          @param reason: str use to advertise reason of lost connection
          """
          # from twisted.internet import reactor
          log.info("Connection failed : %s" % reason)
          reactor.stop()
          app.exit()
  
  
  def autoDetectKeyboardLayout():
      """
      @summary: try to auto detect keyboard layout
      """
      try:
          if os.name == 'posix':
              from subprocess import check_output
              result = check_output(["setxkbmap", "-print"])
              if 'azerty' in result:
                  return "fr"
          elif os.name == 'nt':
              import win32api, win32con, win32process
              from ctypes import windll
              w = windll.user32.GetForegroundWindow()
              tid = windll.user32.GetWindowThreadProcessId(w, 0)
              result = windll.user32.GetKeyboardLayout(tid)
              log.info(result)
              if result == 0x40c040c:
                  return "fr"
      except Exception as e:
          log.info("failed to auto detect keyboard layout " + str(e))
          pass
      return "en"
  
  
  def create_rpds(passw, ip):
      keyboardLayout = autoDetectKeyboardLayout()
      domain = ""
      fullscreen = False
      optimized = False
      recodedPath = None
      width = 1024
      height = 800
  
      if fullscreen:
          width = QtGui.QDesktopWidget().screenGeometry().width()
          height = QtGui.QDesktopWidget().screenGeometry().height()
  
      log.info("keyboard layout set to %s" % keyboardLayout)
  
      connector = reactor.connectTCP(ip, 3389,
                                     RDPClientQtFactory(width, height, "Administrator", passw, domain, fullscreen,
                                                        keyboardLayout,
                                                        optimized, "nego", recodedPath))
      reactor.run()
      # reactor.runReturn()
      # print i
      # tr.start()
      app.exec_()
  
      # reactor.callLater(5000, create_rpds)
      # time.sleep(10)
      # connector.disconnect()
      # reactor.stop()
      #print 'gfgddsfdsfsdffgfd'
  
  
  def exits():
      # time.sleep(10)
      print "before stop"
      reactor.stop()
      # del(reactor)
  
      print "after stop"
      # time.sleep(10)
      print "before app_ehit"
  
      app.exit()
      print "after app"
  
  
  import qt4reactor
  
  qt4reactor.install()
  
  with open('json_file.json', 'r') as f:
      data = json.load(f)
  app = QtGui.QApplication(sys.argv)
  for i in data:
      from twisted.internet import reactor
  
      create_rpds(i["Password"].encode('utf-8'), i["IpAddress"].encode('utf-8'))
  </code></pre>