Cleaned up code and fixed a bug when exporting to Mac as well as other platforms.

This commit is contained in:
Joey Payne 2014-04-02 11:45:13 -06:00
commit 40609a60dc
2 changed files with 131 additions and 79 deletions

185
main.py
View file

@ -1,4 +1,4 @@
from utils import zip_files, join_files from utils import zip_files, join_files, log, get_temp_dir
import sys, os, glob, json, re, shutil, stat, tarfile, zipfile, traceback, platform import sys, os, glob, json, re, shutil, stat, tarfile, zipfile, traceback, platform
from PySide import QtGui, QtCore from PySide import QtGui, QtCore
from PySide.QtGui import QApplication from PySide.QtGui import QApplication
@ -7,26 +7,21 @@ from PySide.QtCore import QUrl, QFileInfo, QFile, QIODevice
from zipfile import ZipFile from zipfile import ZipFile
from tarfile import TarFile from tarfile import TarFile
inside_mac_app = getattr(sys, 'frozen', '')
#if this is a mac application if inside_mac_app:
frozen = getattr(sys, 'frozen', '')
if frozen:
CWD = os.path.dirname(sys.executable) CWD = os.path.dirname(sys.executable)
os.chdir(CWD) os.chdir(CWD)
else: else:
CWD = os.getcwd() CWD = os.getcwd()
if platform.system() == 'Windows': TEMP_DIR = get_temp_dir()
TEMP_DIR = os.path.join('c:/','windows','temp')
else:
TEMP_DIR = os.path.sep+'tmp'
class WThread(QtCore.QThread): class BackgroundThread(QtCore.QThread):
def __init__(self, widget, method_name, parent=None): def __init__(self, widget, method_name, parent=None):
QtCore.QThread.__init__(self, parent) QtCore.QThread.__init__(self, parent)
self.widget=widget self.widget = widget
self.method_name = method_name self.method_name = method_name
def run(self): def run(self):
@ -43,15 +38,19 @@ class Setting(object):
self.required = required self.required = required
self.type = type self.type = type
self.file_types = file_types self.file_types = file_types
for k, v in kwargs.items():
setattr(self, k, v)
self.default_value = kwargs.pop('default_value', None) self.default_value = kwargs.pop('default_value', None)
self.set_extra_attributes_from_keyword_args(kwargs)
if self.value is None: if self.value is None:
self.value = self.default_value self.value = self.default_value
self.save_path = kwargs.pop('save_path', TEMP_DIR) self.save_path = kwargs.pop('save_path', TEMP_DIR)
self.get_file_information_from_url()
def get_file_information_from_url(self):
if hasattr(self, 'url'): if hasattr(self, 'url'):
self.file_name = self.url.split('/')[-1] self.file_name = self.url.split('/')[-1]
self.full_file_path = os.path.join(self.save_path, self.file_name) self.full_file_path = os.path.join(self.save_path, self.file_name)
@ -63,6 +62,10 @@ class Setting(object):
self.extract_class = TarFile.open self.extract_class = TarFile.open
self.extract_args = ('r:gz',) self.extract_args = ('r:gz',)
def set_extra_attributes_from_keyword_args(self, kwargs):
for undefined_key, undefined_value in kwargs.items():
setattr(self, undefined_key, undefined_value)
def get_file_bytes(self): def get_file_bytes(self):
fbytes = None fbytes = None
file = self.extract_class(self.full_file_path, *self.extract_args) file = self.extract_class(self.full_file_path, *self.extract_args)
@ -72,10 +75,10 @@ class Setting(object):
fbytes = file.read(self.extract_file) fbytes = file.read(self.extract_file)
return fbytes return fbytes
def __repr__(self): def __repr__(self):
return 'Setting: (name={}, display_name={}, value={}, required={}, type={})'.format(self.name, self.display_name, self.value, self.required, self.type) return 'Setting: (name={}, display_name={}, value={}, required={}, type={})'.format(self.name, self.display_name, self.value, self.required, self.type)
class MainWindow(QtGui.QWidget): class MainWindow(QtGui.QWidget):
base_url = 'http://node-webkit.s3-website-us-east-1.amazonaws.com/v0.9.2/' base_url = 'http://node-webkit.s3-website-us-east-1.amazonaws.com/v0.9.2/'
@ -133,6 +136,7 @@ class MainWindow(QtGui.QWidget):
application_setting_order = ['main', 'node-main', 'name', 'description', 'version', 'keywords', application_setting_order = ['main', 'node-main', 'name', 'description', 'version', 'keywords',
'nodejs', 'single-instance', 'plugin', 'nodejs', 'single-instance', 'plugin',
'java', 'page-cache'] 'java', 'page-cache']
window_setting_order = ['title', 'icon', 'width', 'height', 'min_width', 'min_height', window_setting_order = ['title', 'icon', 'width', 'height', 'min_width', 'min_height',
'max_width', 'max_height', 'toolbar', 'always-on-top', 'frame', 'max_width', 'max_height', 'toolbar', 'always-on-top', 'frame',
'show_in_taskbar', 'visible', 'resizable', 'fullscreen'] 'show_in_taskbar', 'visible', 'resizable', 'fullscreen']
@ -150,58 +154,82 @@ class MainWindow(QtGui.QWidget):
self.extract_error = None self.extract_error = None
layout = QtGui.QVBoxLayout() self.create_application_layout()
dl_bar = self.createDownloadBar()
self.app_settings_widget = self.createApplicationSettings()
self.win_settings_widget = self.createWindowSettings()
self.ex_settings_widget = self.createExportSettings()
self.app_settings_widget.setEnabled(False) self.option_settings_enabled(False)
self.win_settings_widget.setEnabled(False)
self.ex_settings_widget.setEnabled(False)
layout.addWidget(self.createDirectoryChoose())
layout.addWidget(self.app_settings_widget)
layout.addWidget(self.win_settings_widget)
layout.addWidget(self.ex_settings_widget)
layout.addLayout(dl_bar)
self.setLayout(layout)
self.setWindowTitle("Web2Executable") self.setWindowTitle("Web2Executable")
def create_application_layout(self):
self.main_layout = QtGui.QVBoxLayout()
self.create_layout_widgets()
self.add_widgets_to_main_layout()
self.setLayout(self.main_layout)
def create_layout_widgets(self):
self.download_bar_widget = self.createDownloadBar()
self.app_settings_widget = self.createApplicationSettings()
self.win_settings_widget = self.createWindowSettings()
self.ex_settings_widget = self.createExportSettings()
self.directory_chooser_widget = self.createDirectoryChoose()
def add_widgets_to_main_layout(self):
self.main_layout.addWidget(self.directory_chooser_widget)
self.main_layout.addWidget(self.app_settings_widget)
self.main_layout.addWidget(self.win_settings_widget)
self.main_layout.addWidget(self.ex_settings_widget)
self.main_layout.addLayout(self.download_bar_widget)
def option_settings_enabled(self, is_enabled):
self.ex_button.setEnabled(is_enabled)
self.app_settings_widget.setEnabled(is_enabled)
self.win_settings_widget.setEnabled(is_enabled)
self.ex_settings_widget.setEnabled(is_enabled)
def export(self, export_button, cancel_button): def export(self, export_button, cancel_button):
self.get_files_to_download()
self.try_to_download_files()
def get_files_to_download(self):
self.files_to_download = [] self.files_to_download = []
for setting_name, setting in self.export_settings.items(): for setting_name, setting in self.export_settings.items():
if setting.value == True: if setting.value == True:
self.files_to_download.append(setting) self.files_to_download.append(setting)
def try_to_download_files(self):
if self.files_to_download: if self.files_to_download:
self.progress_bar.setVisible(True) self.progress_bar.setVisible(True)
cancel_button.setEnabled(True) self.cancel_button.setEnabled(True)
self.disableUIWhileWorking() self.disableUIWhileWorking()
setting = self.files_to_download.pop()
try:
self.downloadFile(setting.url, setting)
except Exception as e:
if os.path.exists(setting.full_file_path):
os.remove(setting.full_file_path)
QtGui.QMessageBox.information(self, 'Error!', str(e))
self.download_file_with_error_handling()
else: else:
#This shouldn't happen since we disable the UI if there are no options selected
#But in the weird event that this does happen, we are prepared!
QtGui.QMessageBox.information(self, 'Export Options Empty!', 'Please choose one of the export options!') QtGui.QMessageBox.information(self, 'Export Options Empty!', 'Please choose one of the export options!')
def disableUIWhileWorking(self): def download_file_with_error_handling(self):
self.ex_button.setEnabled(False) setting = self.files_to_download.pop()
self.app_settings_widget.setEnabled(False) try:
self.win_settings_widget.setEnabled(False) self.downloadFile(setting.url, setting)
self.ex_settings_widget.setEnabled(False) except Exception as e:
if os.path.exists(setting.full_file_path):
os.remove(setting.full_file_path)
error = ''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))
self.show_error(error)
def show_error(self, exception):
QtGui.QMessageBox.information(self, 'Error!', str(exception))
def disableUIWhileWorking(self):
self.option_settings_enabled(False)
def enableUI(self): def enableUI(self):
self.ex_button.setEnabled(True) self.option_settings_enabled(True)
self.app_settings_widget.setEnabled(True)
self.win_settings_widget.setEnabled(True)
self.ex_settings_widget.setEnabled(True)
def requiredSettingsFilled(self): def requiredSettingsFilled(self):
proj_dir = self.projectDir() proj_dir = self.projectDir()
@ -219,7 +247,7 @@ class MainWindow(QtGui.QWidget):
if setting.required and not setting.value: if setting.required and not setting.value:
return False return False
if setting.type == 'file' and setting.value and not os.path.exists(os.path.join(self.projectDir(),setting.value)): if setting.type == 'file' and setting.value and not os.path.exists(os.path.join(self.projectDir(),setting.value)):
print setting.value, "does not exist" log(setting.value, "does not exist")
settings_valid = False settings_valid = False
export_chosen = False export_chosen = False
@ -278,7 +306,6 @@ class MainWindow(QtGui.QWidget):
http.dataReadProgress.connect(self.updateProgressBar) http.dataReadProgress.connect(self.updateProgressBar)
http.responseHeaderReceived.connect(self.readResponseHeader) http.responseHeaderReceived.connect(self.readResponseHeader)
self.http = http self.http = http
self.cancel_button = cancel_button
self.ex_button = ex_button self.ex_button = ex_button
return hlayout return hlayout
@ -286,8 +313,7 @@ class MainWindow(QtGui.QWidget):
def readResponseHeader(self, response_header): def readResponseHeader(self, response_header):
# Check for genuine error conditions. # Check for genuine error conditions.
if response_header.statusCode() not in (200, 300, 301, 302, 303, 307): if response_header.statusCode() not in (200, 300, 301, 302, 303, 307):
QtGui.QMessageBox.information(self, 'Error', self.show_error('Download failed: {}.'.format(response_header.reasonPhrase()))
'Download failed: %s.' % response_header.reasonPhrase())
self.httpRequestAborted = True self.httpRequestAborted = True
self.http.abort() self.http.abort()
@ -306,8 +332,7 @@ class MainWindow(QtGui.QWidget):
if error: if error:
self.outFile.remove() self.outFile.remove()
QtGui.QMessageBox.information(self, 'Error', self.show_error('Download failed: {}.'.format(self.http.errorString()))
'Download failed: %s.' % self.http.errorString())
self.continueDownloadingOrExtract() self.continueDownloadingOrExtract()
@ -316,14 +341,22 @@ class MainWindow(QtGui.QWidget):
setting = self.files_to_download.pop() setting = self.files_to_download.pop()
self.downloadFile(setting.url, setting) self.downloadFile(setting.url, setting)
else: else:
self.progress_label.setText('Done') self.progress_text = 'Done.'
self.cancel_button.setEnabled(False) self.cancel_button.setEnabled(False)
self.progress_bar.setVisible(False) self.progress_bar.setVisible(False)
self.extractFilesInBackground() self.extractFilesInBackground()
@property
def progress_text(self):
return self.progress_label.text()
@progress_text.setter
def progress_text(self, value):
self.progress_label.setText(str(value))
def runInBackground(self, method_name, callback): def runInBackground(self, method_name, callback):
self.thread = WThread(self, method_name) self.thread = BackgroundThread(self, method_name)
self.thread.finished.connect(callback) self.thread.finished.connect(callback)
self.thread.start() self.thread.start()
@ -334,13 +367,13 @@ class MainWindow(QtGui.QWidget):
def doneMakingFiles(self): def doneMakingFiles(self):
self.ex_button.setEnabled(True) self.ex_button.setEnabled(True)
self.progress_label.setText('Done Exporting.') self.progress_text = 'Done Exporting.'
self.enableUI() self.enableUI()
if self.output_err: if self.output_err:
QtGui.QMessageBox.information(self, 'Error!', str(self.output_err)) self.show_error(self.output_err)
def extractFilesInBackground(self): def extractFilesInBackground(self):
self.progress_label.setText('Extracting') self.progress_text = 'Extracting.'
self.ex_button.setEnabled(False) self.ex_button.setEnabled(False)
self.runInBackground('extractFiles', self.doneExtracting) self.runInBackground('extractFiles', self.doneExtracting)
@ -360,7 +393,7 @@ class MainWindow(QtGui.QWidget):
if os.path.exists(setting.full_file_path): if os.path.exists(setting.full_file_path):
os.remove(setting.full_file_path) #remove the zip/tar since we don't need it anymore os.remove(setting.full_file_path) #remove the zip/tar since we don't need it anymore
self.progress_label.setText(self.progress_label.text()+'.') self.progress_text += '.'
except (tarfile.ReadError, zipfile.BadZipfile) as e: except (tarfile.ReadError, zipfile.BadZipfile) as e:
if os.path.exists(setting.full_file_path): if os.path.exists(setting.full_file_path):
@ -373,14 +406,14 @@ class MainWindow(QtGui.QWidget):
def doneExtracting(self): def doneExtracting(self):
self.ex_button.setEnabled(True) self.ex_button.setEnabled(True)
if self.extract_error: if self.extract_error:
self.progress_label.setText('Error extracting.') self.progress_text = 'Error extracting.'
QtGui.QMessageBox.information(self, 'Error!', 'There were one or more errors with your zip/tar files. They were deleted. Please try to export again.') self.show_error('There were one or more errors with your zip/tar files. They were deleted. Please try to export again.')
else: else:
self.progress_label.setText('Done Extracting.') self.progress_text = 'Done extracting.'
self.makeOutputFilesInBackground() self.makeOutputFilesInBackground()
def cancelDownload(self): def cancelDownload(self):
self.progress_label.setText("Download canceled.") self.progress_text = 'Download cancelled.'
self.cancel_button.setEnabled(False) self.cancel_button.setEnabled(False)
self.httpRequestAborted = True self.httpRequestAborted = True
self.http.abort() self.http.abort()
@ -393,7 +426,7 @@ class MainWindow(QtGui.QWidget):
self.progress_bar.setValue(bytesRead) self.progress_bar.setValue(bytesRead)
def downloadFile(self, path, setting): def downloadFile(self, path, setting):
self.progress_label.setText('Downloading {}'.format(path.replace(self.base_url,''))) self.progress_text = 'Downloading {}'.format(path.replace(self.base_url,''))
url = QUrl(path) url = QUrl(path)
fileInfo = QFileInfo(url.path()) fileInfo = QFileInfo(url.path())
@ -405,8 +438,7 @@ class MainWindow(QtGui.QWidget):
self.outFile = QFile(fileName) self.outFile = QFile(fileName)
if not self.outFile.open(QIODevice.WriteOnly): if not self.outFile.open(QIODevice.WriteOnly):
QtGui.QMessageBox.information(self, 'Error', self.show_error('Unable to save the file {}: {}.'.format(fileName, self.outFile.errorString()))
'Unable to save the file %s: %s.' % (fileName, self.outFile.errorString()))
self.outFile = None self.outFile = None
self.enableUI() self.enableUI()
return return
@ -722,7 +754,7 @@ class MainWindow(QtGui.QWidget):
try: try:
self.load_from_json(json_str) self.load_from_json(json_str)
except ValueError: #Json file is invalid except ValueError: #Json file is invalid
print 'Warning: Json file invalid.' log( 'Warning: Json file invalid.')
def load_from_json(self, json_str): def load_from_json(self, json_str):
@ -759,21 +791,21 @@ class MainWindow(QtGui.QWidget):
setting.value = os.path.basename(setting.value) setting.value = os.path.basename(setting.value)
shutil.copy(setting.value, self.projectDir()) shutil.copy(setting.value, self.projectDir())
except shutil.Error as e:#same file warning except shutil.Error as e:#same file warning
print 'Warning: {}'.format(e) log( 'Warning: {}'.format(e))
os.chdir(old_dir) os.chdir(old_dir)
def makeOutputDirs(self): def makeOutputDirs(self):
self.output_err = '' self.output_err = ''
try: try:
self.progress_label.setText('Removing old output directory...') self.progress_text = 'Removing old output directory...'
outputDir = os.path.join(self.outputDir(), self.projectName()) outputDir = os.path.join(self.outputDir(), self.projectName())
tempDir = os.path.join(TEMP_DIR, 'webexectemp') tempDir = os.path.join(TEMP_DIR, 'webexectemp')
if os.path.exists(tempDir): if os.path.exists(tempDir):
shutil.rmtree(tempDir) shutil.rmtree(tempDir)
self.progress_label.setText('Making new directories...') self.progress_text = 'Making new directories...'
if not os.path.exists(outputDir): if not os.path.exists(outputDir):
os.makedirs(outputDir) os.makedirs(outputDir)
@ -792,7 +824,7 @@ class MainWindow(QtGui.QWidget):
zip_files(zip_file, self.projectDir(), exclude_paths=[outputDir]) zip_files(zip_file, self.projectDir(), exclude_paths=[outputDir])
for ex_setting in self.export_settings.values(): for ex_setting in self.export_settings.values():
if ex_setting.value: if ex_setting.value:
self.progress_label.setText('Making files for {}'.format(ex_setting.display_name)) self.progress_text = 'Making files for {}'.format(ex_setting.display_name)
export_dest = os.path.join(outputDir, ex_setting.name) export_dest = os.path.join(outputDir, ex_setting.name)
if os.path.exists(export_dest): if os.path.exists(export_dest):
@ -800,16 +832,17 @@ class MainWindow(QtGui.QWidget):
#shutil will make the directory for us #shutil will make the directory for us
shutil.copytree(os.path.join('files', ex_setting.name), export_dest) shutil.copytree(os.path.join('files', ex_setting.name), export_dest)
self.progress_label.setText(self.progress_label.text()+'.') self.progress_text += '.'
if ex_setting.name == 'mac': if ex_setting.name == 'mac':
app_path = os.path.join(export_dest, self.projectName()+'.app') app_path = os.path.join(export_dest, self.projectName()+'.app')
shutil.move(os.path.join(export_dest, 'node-webkit.app'), app_path) shutil.move(os.path.join(export_dest, 'node-webkit.app'), app_path)
self.progress_label.setText(self.progress_label.text()+'.') self.progress_text += '.'
shutil.move(zip_file, os.path.join(app_path, 'Contents', 'Resources', 'app.nw')) shutil.copy(zip_file, os.path.join(app_path, 'Contents', 'Resources', 'app.nw'))
self.progress_label.setText(self.progress_label.text()+'.') self.progress_text += '.'
else: else:
ext = '' ext = ''
if ex_setting.name == 'windows': if ex_setting.name == 'windows':
@ -822,9 +855,11 @@ class MainWindow(QtGui.QWidget):
sevenfivefive = stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH sevenfivefive = stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH
os.chmod(dest_binary_path, sevenfivefive) os.chmod(dest_binary_path, sevenfivefive)
self.progress_label.setText(self.progress_label.text()+'.') self.progress_text += '.'
if os.path.exists(nw_path): if os.path.exists(nw_path):
os.remove(nw_path) os.remove(nw_path)
except Exception as e: except Exception as e:
self.output_err += ''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) self.output_err += ''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))
finally: finally:

View file

@ -1,6 +1,23 @@
import os, zipfile, io from __future__ import print_function
import os, zipfile, io, platform
import sys import sys
DEBUG = False
def is_windows():
return platform.system() == 'Windows'
def get_temp_dir():
if is_windows():
temp_dir = os.path.join('c:/','windows','temp')
else:
temp_dir = os.path.sep+'tmp'
return temp_dir
def log(*args):
if DEBUG:
print(*args)
def zip_files(zip_file_name, *args, **kwargs): def zip_files(zip_file_name, *args, **kwargs):
zip_file = zipfile.ZipFile(zip_file_name, 'w') zip_file = zipfile.ZipFile(zip_file_name, 'w')
verbose = kwargs.pop('verbose', False) verbose = kwargs.pop('verbose', False)
@ -22,12 +39,12 @@ def zip_files(zip_file_name, *args, **kwargs):
for file in files: for file in files:
file_loc = os.path.relpath(os.path.join(root, file), directory) file_loc = os.path.relpath(os.path.join(root, file), directory)
if verbose: if verbose:
print file_loc log(file_loc)
zip_file.write(file_loc) zip_file.write(file_loc)
for direc in dirs: for direc in dirs:
dir_loc = os.path.relpath(os.path.join(root, direc), directory) dir_loc = os.path.relpath(os.path.join(root, direc), directory)
if verbose: if verbose:
print dir_loc log(dir_loc)
zip_file.write(dir_loc) zip_file.write(dir_loc)
else: else:
@ -36,7 +53,7 @@ def zip_files(zip_file_name, *args, **kwargs):
os.chdir(directory) os.chdir(directory)
file_loc = os.path.relpath(arg, directory) file_loc = os.path.relpath(arg, directory)
if verbose: if verbose:
print file_loc log(file_loc)
zip_file.write(file_loc) zip_file.write(file_loc)
os.chdir(old_path) os.chdir(old_path)