import os
import sys
import pkg_resources # make PyInstaller import this
import psutil
import subprocess
import time
import threading

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *
from PyQt4.QtNetwork import *

class Portablizer(QMainWindow):
  def __init__(self):
    QMainWindow.__init__(self);
    self.resize(1024, 768)
    g = self.frameGeometry()
    g.moveCenter(QDesktopWidget().availableGeometry().center())
    self.move(g.topLeft())
    self.browser = QWebView()
    self.setCentralWidget(self.browser)

  def startBrowser(self):
    self.browser.setHtml('<strong>Loading...</strong>')
    self.browser.show()

  def goto(self, url):
    self.browser.load(QUrl(url))

if getattr(sys, 'frozen', False):
  basedir = sys._MEIPASS
else:
  basedir = os.path.dirname(os.path.abspath(__file__))

def nodedir(file):
  return os.path.join(basedir, "node", file)

nodeurl = 'http://127.0.0.1:3000/'

app = QApplication([])
app.setApplicationName('Portablizer')

portablizer = Portablizer()
portablizer.setWindowTitle('Portablizer')

# kill previous launched but not ended node process
for proc in psutil.process_iter():
  try:
    if proc.name == "node.exe" and proc.getcwd() == basedir:
      proc.kill()
  except:
    pass

# hide console window only in Windows exe
startupinfo = None
try:
  startupinfo = subprocess.STARTUPINFO()
  startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
except:
  pass

# can't use stdout in Windows because there is no console
nodeprocess = None
try:
  subp = [nodedir("node.exe"), nodedir("app.js")]
  if startupinfo is not None:
    nodeprocess = subprocess.Popen(subp, startupinfo=startupinfo)
  else:
    nodeprocess = subprocess.Popen(subp, stdout=subprocess.PIPE)
except:
  QMessageBox.warning(portablizer, "Error", "The application is missing required files. You need to re-install it.")
  if hasattr(nodeprocess, 'kill'):
    nodeprocess.kill()
  sys.exit(1)

portablizer.show()
portablizer.startBrowser()

if startupinfo is not None:
  # in Windows, delay one second
  threading.Timer(1, portablizer.goto(nodeurl))
else:
  # in unix, read node output, when server is ready, goto page
  for line in iter(nodeprocess.stdout.readline, ''):
    if "3000" in line:
      portablizer.goto(nodeurl)
      break

menubar = portablizer.menuBar()
helpMenu = menubar.addMenu('&Options')
openweb = QAction('Reload', app)
openweb.triggered.connect(lambda: portablizer.goto(nodeurl))
helpMenu.addAction(openweb)

appprocess = app.exec_()

if hasattr(nodeprocess, 'kill'):
  nodeprocess.kill()

sys.exit(appprocess)

@@ -109,29 +109,14 @@
 ort 
-time%0Aimport threading
+socket
 %0A%0Afr
@@ -752,16 +752,35 @@
 _MEIPASS
+ # PyInstaller path
 %0Aelse:%0A 
@@ -899,16 +899,358 @@
  file)%0A%0A
+# if you call this function,%0A# system would ask user for internet permission for the first time%0Adef findAvailablePort():%0A  newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)%0A  newSocket.bind((%22%22, 0))%0A  newSocket.listen(1)%0A  newPort = newSocket.getsockname()%5B1%5D%0A  newSocket.close()%0A  return newPort%0A%0Aport = str(findAvailablePort())%0A
 nodeurl 
@@ -1269,20 +1269,28 @@
 7.0.0.1:
-3000
+' + port + '
 /'%0A%0Aapp 
@@ -1652,16 +1652,52 @@
 dows exe
+, so don't use stdout in latter code
 %0Astartup
@@ -1831,307 +1831,202 @@
 ss%0A%0A
-# can't use stdout in Windows because there is no console%0Anodeprocess = None%0Atry:%0A  subp = %5Bnodedir(%22node.exe%22), nodedir(%22app.js%22)%5D%0A  if startupinfo is not None:%0A    nodeprocess = subprocess.Popen(subp, startupinfo=startupinfo)%0A  else:%0A    nodeprocess = subprocess.Popen(subp, stdout=subprocess.PIPE
+nodeprocess = None%0Atry:%0A  env = os.environ.copy()%0A  env%5B'PORTABLIZER_PORT'%5D = port%0A  nodeprocess = subprocess.Popen(%5Bnodedir(%22node.exe%22), nodedir(%22app.js%22)%5D,%0A    startupinfo=startupinfo, env=env
 )%0Aex
@@ -2074,16 +2074,20 @@
 %22Error%22,
+%0A   
  %22The ap
@@ -2275,306 +2275,165 @@
 r()%0A
-%0Aif startupinfo is not None:%0A  # in Windows, delay one second%0A  threading.Timer(1, portablizer.goto(nodeurl))%0Aelse:%0A  # in unix, read node output, when server is ready, goto page%0A  for line in iter(nodeprocess.stdout.readline, ''):%0A    if %223000%22 in line:%0A      portablizer.goto(nodeurl)%0A      break
+portablizer.browser.setHtml('''%0A  %3Cscript%3EsetTimeout(function()%7B window.location.href=%22%25s%22; %7D, 1000);%3C/script%3E%0A  %3Cstrong%3ELoading...%3C/strong%3E%0A  ''' %25 nodeurl)
 %0A%0Ame

clean up; increase stability; add portablizer port;