diff --git a/build_windows.bat b/build_windows.bat old mode 100644 new mode 100755 diff --git a/command_line.py b/command_line.py index 94847f8..93e7e4c 100644 --- a/command_line.py +++ b/command_line.py @@ -5,7 +5,9 @@ from pycns import save_icns from pepy.pe import PEFile import urllib2 +import platform import re +import time import sys import os import glob @@ -36,6 +38,11 @@ inside_packed_exe = getattr(sys, 'frozen', '') CWD = os.getcwd() +def get_file(path): + parts = path.split('/') + independent_path = os.path.join(CWD, *parts) + return independent_path + TEMP_DIR = get_temp_dir() DEFAULT_DOWNLOAD_PATH = os.path.join(CWD, 'files', @@ -99,6 +106,7 @@ class Setting(object): self.filter = kwargs.pop('filter', '.*') self.filter_action = kwargs.pop('filter_action', 'None') self.check_action = kwargs.pop('check_action', 'None') + self.action = kwargs.pop('action', None) self.set_extra_attributes_from_keyword_args(**kwargs) @@ -309,9 +317,13 @@ class CommandBase(object): for setting_group, setting_group_dict in sgroup_items: settings['setting_groups'].append(settings[setting_group]) + self._setting_items = (config['setting_groups'].items() + + [('export_settings', config['export_settings'])] + + [('compression', config['compression'])]) config.pop('setting_groups') config.pop('export_settings') config.pop('compression') + self._setting_items += config.items() for key, val in config.items(): settings[key] = val @@ -450,7 +462,8 @@ class CommandBase(object): dl_export_items = (self.settings['download_settings'].items() + self.settings['export_settings'].items() + - self.settings['compression'].items()) + self.settings['compression'].items() + + self.settings['web2exe_settings'].items()) for setting_name, setting in dl_export_items: if setting.value is not None: dic['webexe_settings'][setting_name] = setting.value @@ -513,6 +526,8 @@ class CommandBase(object): if setting.type == 'list': val_str = self.convert_val_to_str(new_dic[item]) setting.value = val_str + if setting.type == 'range': + setting.value = new_dic[item] if isinstance(new_dic[item], dict): stack.append((item, new_dic[item])) return setting_list @@ -678,7 +693,7 @@ class CommandBase(object): if windows: self.replace_icon_in_exe(nw_path) - #self.compress_nw(nw_path) + self.compress_nw(nw_path) dest_binary_path = os.path.join(export_dest, self.project_name() + @@ -709,15 +724,29 @@ class CommandBase(object): def compress_nw(self, nw_path): compression = self.get_setting('nw_compression_level') - upx_bin = os.path.join('files', 'compressors', 'upx-linux-x64') - cmd = '{upx_bin} --lzma -{comp_level} -o test {path}'.format(upx_bin=upx_bin, - comp_level=compression.value, - path=nw_path) - print cmd - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, shell=True) - output, err = proc.communicate() - print err, output + comp_dict = {'Darwin64bit': get_file('files/compressors/upx-mac'), + 'Darwin32bit': get_file('files/compressors/upx-mac'), + 'Linux64bit': get_file('files/compressors/upx-linux-x64'), + 'Linux32bit': get_file('files/compressors/upx-linux-x32'), + 'Windows64bit': get_file('files/compressors/upx-win.exe'), + 'Windows32bit': get_file('files/compressors/upx-win.exe') + } + + plat = platform.system()+platform.architecture()[0] + upx_version = comp_dict.get(plat, None) + + if upx_version is not None: + upx_bin = os.path.join('files', 'compressors', upx_version) + os.chmod(upx_bin, 0755) + cmd = [upx_bin, '--lzma', '-{}'.format(compression.value), str(nw_path)] + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + self.progress_text = '\nCompressing files' + while proc.poll() is None: + self.progress_text += '.' + time.sleep(5) + output, err = proc.communicate() def copy_files_to_project_folder(self): old_dir = CWD diff --git a/files/compressors/upx-linux-x32 b/files/compressors/upx-linux-x32 new file mode 100755 index 0000000..02914a2 Binary files /dev/null and b/files/compressors/upx-linux-x32 differ diff --git a/files/compressors/upx-linux-x64 b/files/compressors/upx-linux-x64 new file mode 100755 index 0000000..4309afc Binary files /dev/null and b/files/compressors/upx-linux-x64 differ diff --git a/files/compressors/upx-mac b/files/compressors/upx-mac new file mode 100755 index 0000000..e931ae1 Binary files /dev/null and b/files/compressors/upx-mac differ diff --git a/files/compressors/upx-win.exe b/files/compressors/upx-win.exe new file mode 100755 index 0000000..1c331c7 Binary files /dev/null and b/files/compressors/upx-win.exe differ diff --git a/files/images/app_settings.png b/files/images/app_settings.png new file mode 100644 index 0000000..778c62c Binary files /dev/null and b/files/images/app_settings.png differ diff --git a/files/images/app_settings.svg b/files/images/app_settings.svg new file mode 100644 index 0000000..63f584b --- /dev/null +++ b/files/images/app_settings.svg @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/files/images/compress_settings.png b/files/images/compress_settings.png new file mode 100644 index 0000000..68a8d4b Binary files /dev/null and b/files/images/compress_settings.png differ diff --git a/files/images/compress_settings.svg b/files/images/compress_settings.svg new file mode 100644 index 0000000..792299d --- /dev/null +++ b/files/images/compress_settings.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/files/images/download_settings.png b/files/images/download_settings.png new file mode 100644 index 0000000..c59cbfb Binary files /dev/null and b/files/images/download_settings.png differ diff --git a/files/images/download_settings.svg b/files/images/download_settings.svg new file mode 100644 index 0000000..7c8f7eb --- /dev/null +++ b/files/images/download_settings.svg @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/files/images/export_settings.png b/files/images/export_settings.png new file mode 100644 index 0000000..dfbe54f Binary files /dev/null and b/files/images/export_settings.png differ diff --git a/files/images/export_settings.svg b/files/images/export_settings.svg new file mode 100644 index 0000000..7d21145 --- /dev/null +++ b/files/images/export_settings.svg @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/files/images/warning.png b/files/images/warning.png new file mode 100644 index 0000000..41bc6d5 Binary files /dev/null and b/files/images/warning.png differ diff --git a/files/images/warning.svg b/files/images/warning.svg new file mode 100644 index 0000000..4e0e7c1 --- /dev/null +++ b/files/images/warning.svg @@ -0,0 +1,84 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/files/images/window_settings.png b/files/images/window_settings.png new file mode 100644 index 0000000..62762fe Binary files /dev/null and b/files/images/window_settings.png differ diff --git a/files/images/window_settings.svg b/files/images/window_settings.svg new file mode 100644 index 0000000..fd633fa --- /dev/null +++ b/files/images/window_settings.svg @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/files/settings.cfg b/files/settings.cfg index 6766640..80bd8b0 100644 --- a/files/settings.cfg +++ b/files/settings.cfg @@ -108,6 +108,11 @@ linux_64_dir_prefix = 'node-webkit-v{}-linux-x64' default_value=False type='check' + [[web2exe_settings]] + [[[export_dir]]] + default_value='' + type='string' + [[window_settings]] [[[title]]] default_value='' @@ -116,15 +121,18 @@ linux_64_dir_prefix = 'node-webkit-v{}-linux-x64' display_name='Window Icon' default_value='' type='file' + action='set_window_icon' file_types='*.png *.jpg *.jpeg' [[[mac_icon]]] default_value='' type='file' + action='set_mac_icon' file_types='*.png *.jpg *.jpeg *.icns' description='This icon to be displayed for the Mac Application. Defaults to Window Icon' [[[exe_icon]]] default_value='' type='file' + action='set_exe_icon' file_types='*.png *.jpg *.jpeg' description='This icon to be displayed for the windows exe of the app. Defaults to Window icon.' [[[width]]] @@ -323,10 +331,12 @@ linux_64_dir_prefix = 'node-webkit-v{}-linux-x64' [compression] [[nw_compression_level]] - display_name='NW Compression' - default_value=5 + display_name='Compression Level' + default_value=0 + min=0 + max=9 type='range' - description='Compression to be applied to the executable\'s node webkit binary. 0 is no compression, 9 is maximum. They all use lzma.' + description='Compression to be applied to the executable\'s nwjs binary. 0 is no compression, 9 is maximum. They all use lzma.' [order] @@ -342,6 +352,7 @@ linux_64_dir_prefix = 'node-webkit-v{}-linux-x64' 'kiosk', 'kiosk_emulation', 'transparent']""" export_setting_order = """['windows-x32', 'windows-x64', 'mac-x32', 'mac-x64', 'linux-x64', 'linux-x32']""" + compression_setting_order = """['nw_compression_level']""" download_setting_order = """['nw_version', 'download_dir', 'force_download']""" diff --git a/image_tool_build.bat b/image_tool_build.bat old mode 100644 new mode 100755 diff --git a/main.py b/main.py index 696cd6d..b19da82 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,6 @@ from utils import log, open_folder_in_explorer -__gui_version__ = "v0.2.0b" +__gui_version__ = "v0.2.1b" import os import re @@ -8,12 +8,14 @@ import glob import sys from PySide import QtGui, QtCore -from PySide.QtGui import QApplication +from PySide.QtGui import QApplication, QHBoxLayout, QVBoxLayout from PySide.QtNetwork import QHttp from PySide.QtCore import QUrl, QFile, QIODevice, QCoreApplication -from command_line import CWD, CommandBase, logger +from command_line import CommandBase, logger, get_file + +MAX_RECENT = 10 class Validator(QtGui.QRegExpValidator): @@ -45,15 +47,104 @@ class BackgroundThread(QtCore.QThread): func() -class MainWindow(QtGui.QWidget, CommandBase): +class MainWindow(QtGui.QMainWindow, CommandBase): def update_nw_versions(self, button): self.get_versions_in_background() + def load_recent_projects(self): + files = [] + history_file = get_file('files/recent_files.txt') + + with open(history_file, 'a+') as f: + for line in f: + line = line.strip() + if line and os.path.exists(line): + files.append(line) + files.reverse() + return files + + def load_last_project_path(self): + proj_path = '' + proj_file = get_file('files/last_project_path.txt') + with open(proj_file, 'a+') as f: + proj_path = f.read().strip() + + if not proj_path: + proj_path = QtCore.QDir.currentPath() + + return proj_path + + def save_project_path(self, path): + proj_file = get_file('files/last_project_path.txt') + with open(proj_file, 'w+') as f: + f.write(path) + + def save_recent_project(self, proj): + recent_file_path = get_file('files/recent_files.txt') + max_length = MAX_RECENT + recent_files = open(recent_file_path, 'a+').read().split('\n') + try: + recent_files.remove(proj) + except ValueError: + pass + recent_files.append(proj) + with open(recent_file_path, 'w+') as f: + for recent_file in recent_files[-max_length:]: + if recent_file and os.path.exists(recent_file): + f.write(recent_file+'\n') + + def update_recent_files(self): + previous_files = self.load_recent_projects() + self.recent_separator.setVisible(len(previous_files) > 0) + for i in xrange(len(previous_files)): + text = '{} - {}'.format(i+1, os.path.basename(previous_files[i])) + action = self.recent_file_actions[i] + action.setText(text) + action.setData(previous_files[i]) + action.setVisible(True) + def __init__(self, width, height, app, parent=None): super(MainWindow, self).__init__(parent) CommandBase.__init__(self) + self.icon_style = 'width:48px;height:48px;background-color:white;border-radius:5px;border:1px solid rgb(50,50,50);' + + self.last_project_dir = self.load_last_project_path() + + status_bar = QtGui.QStatusBar() + self.setStatusBar(status_bar) + + self.project_path = '' + + self.project_menu = self.menuBar().addMenu('File') + browse_action = QtGui.QAction('Open Project', self.project_menu, + shortcut=QtGui.QKeySequence.Open, + statusTip='Open an existing or new project.', + triggered=self.browse_dir) + self.project_menu.addAction(browse_action) + self.project_menu.addSeparator() + + self.recent_file_actions = [] + + for i in xrange(MAX_RECENT): + if i == 9: + key = 0 + else: + key = i+1 + action = QtGui.QAction(self, visible=False, triggered=self.open_recent_file, + shortcut=QtGui.QKeySequence('Ctrl+{}'.format(key))) + self.recent_file_actions.append(action) + self.project_menu.addAction(action) + + self.recent_separator = self.project_menu.addSeparator() + + self.update_recent_files() + + exit_action = QtGui.QAction('Exit', self.project_menu) + exit_action.triggered.connect(QtGui.qApp.closeAllWindows) + self.project_menu.addAction(exit_action) + self.logger = logger self.gui_app = app @@ -62,8 +153,7 @@ class MainWindow(QtGui.QWidget, CommandBase): self.options_enabled = False self.output_package_json = True - self.setWindowIcon(QtGui.QIcon(os.path.join(CWD, 'files', - 'images', 'icon.png'))) + self.setWindowIcon(QtGui.QIcon(get_file('files/images/icon.png'))) self.update_json = False self.setup_nw_versions() @@ -82,10 +172,15 @@ class MainWindow(QtGui.QWidget, CommandBase): self.setWindowTitle("Web2Executable {}".format(__gui_version__)) self.update_nw_versions(None) + def open_recent_file(self): + action = self.sender() + if action: + self.load_project(action.data()) + def setup_nw_versions(self): nw_version = self.get_setting('nw_version') try: - f = open(os.path.join(CWD, 'files', 'nw-versions.txt')) + f = open(get_file('files/nw-versions.txt')) for line in f: nw_version.values.append(line.strip()) except IOError: @@ -93,28 +188,48 @@ class MainWindow(QtGui.QWidget, CommandBase): def create_application_layout(self): self.main_layout = QtGui.QVBoxLayout() + self.tab_widget = QtGui.QTabWidget() self.main_layout.setContentsMargins(10, 5, 10, 5) self.create_layout_widgets() self.addWidgets_to_main_layout() - self.setLayout(self.main_layout) + w = QtGui.QWidget() + w.setLayout(self.main_layout) + + self.setCentralWidget(w) def create_layout_widgets(self): self.download_bar_widget = self.create_download_bar() self.app_settings_widget = self.create_application_settings() + self.comp_settings_widget = self.create_compression_settings() self.win_settings_widget = self.create_window_settings() self.ex_settings_widget = self.create_export_settings() self.dl_settings_widget = self.create_download_settings() self.directory_chooser_widget = self.create_directory_choose() def addWidgets_to_main_layout(self): + self.warning_settings_icon = QtGui.QIcon(get_file('files/images/warning.png')) + self.app_settings_icon = QtGui.QIcon(get_file('files/images/app_settings.png')) + self.win_settings_icon = QtGui.QIcon(get_file('files/images/window_settings.png')) + self.ex_settings_icon = QtGui.QIcon(get_file('files/images/export_settings.png')) + self.comp_settings_icon = QtGui.QIcon(get_file('files/images/compress_settings.png')) + self.download_settings_icon = QtGui.QIcon(get_file('files/images/download_settings.png')) + + self.tab_icons = [self.app_settings_icon, + self.win_settings_icon, + self.ex_settings_icon, + self.comp_settings_icon, + self.download_settings_icon] + 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.addWidget(self.dl_settings_widget) + self.tab_widget.addTab(self.app_settings_widget, self.app_settings_icon, 'App Settings') + self.tab_widget.addTab(self.win_settings_widget, self.win_settings_icon, 'Window Settings') + self.tab_widget.addTab(self.ex_settings_widget, self.ex_settings_icon, 'Export Settings') + self.tab_widget.addTab(self.comp_settings_widget, self.comp_settings_icon, 'Compression Settings') + self.tab_widget.addTab(self.dl_settings_widget, self.download_settings_icon, 'Download Settings') + self.main_layout.addWidget(self.tab_widget) self.main_layout.addLayout(self.download_bar_widget) def option_settings_enabled(self, is_enabled): @@ -122,6 +237,7 @@ class MainWindow(QtGui.QWidget, CommandBase): self.app_settings_widget.setEnabled(is_enabled) self.win_settings_widget.setEnabled(is_enabled) self.ex_settings_widget.setEnabled(is_enabled) + self.comp_settings_widget.setEnabled(is_enabled) self.dl_settings_widget.setEnabled(is_enabled) self.options_enabled = is_enabled @@ -168,23 +284,22 @@ class MainWindow(QtGui.QWidget, CommandBase): self.option_settings_enabled(True) self.directory_chooser_widget.setEnabled(True) + def get_tab_index_for_setting_name(self, name): + options_dict = {'app_settings': 0, + 'webkit_settings': 0, + 'window_settings': 1, + 'export_settings': 2, + 'compression': 3, + 'download_settings': 4} + for setting_group_name, setting_group in self._setting_items: + if name in setting_group: + return options_dict.get(setting_group_name, None) + def required_settings_filled(self, ignore_options=False): if not self.options_enabled and not ignore_options: return False - proj_dir = self.project_dir() - out_dir = self.output_dir() - - valid_proj_dirs = False - - if proj_dir and out_dir: - if os.path.exists(proj_dir): - valid_proj_dirs = True - self.input_line.setStyleSheet('') - self.input_line.setToolTip('') - else: - self.input_line.setStyleSheet('QLineEdit{border:3px solid rgba(238, 68, 83, 200); border-radius:5px;}') - self.input_line.setToolTip('The project directory does not exist.') + red_border = 'QLineEdit{border:3px solid rgba(238, 68, 83, 200); border-radius:5px;}' settings_valid = True for sgroup in self.settings['setting_groups']: @@ -196,8 +311,10 @@ class MainWindow(QtGui.QWidget, CommandBase): settings_valid = False widget = self.find_child_by_name(setting.name) if widget is not None: - widget.setStyleSheet('QLineEdit{border:3px solid rgba(238, 68, 83, 200); border-radius:5px;}') + widget.setStyleSheet(red_border) widget.setToolTip('This setting is required.') + tab = self.get_tab_index_for_setting_name(setting.name) + self.tab_widget.setTabIcon(tab, self.warning_settings_icon) if (setting.type == 'file' and setting.value and @@ -206,8 +323,10 @@ class MainWindow(QtGui.QWidget, CommandBase): settings_valid = False widget = self.find_child_by_name(setting.name) if widget is not None: - widget.setStyleSheet('QLineEdit{border:3px solid rgba(238, 68, 83, 200); border-radius:5px;}') + widget.setStyleSheet(red_border) widget.setToolTip('The file "{}" does not exist.'.format(os.path.join(self.project_dir(),setting.value))) + tab = self.get_tab_index_for_setting_name(setting.name) + self.tab_widget.setTabIcon(tab, self.warning_settings_icon) if (setting.type == 'folder' and setting.value and @@ -215,12 +334,17 @@ class MainWindow(QtGui.QWidget, CommandBase): settings_valid = False widget = self.find_child_by_name(setting.name) if widget is not None: - widget.setStyleSheet('QLineEdit{border:3px solid rgba(238, 68, 83, 200); border-radius:5px;}') + widget.setStyleSheet(red_border) widget.setToolTip('The folder "{}" does not exist'.format(os.path.join(self.project_dir(), setting.value))) + tab = self.get_tab_index_for_setting_name(setting.name) + self.tab_widget.setTabIcon(tab, self.warning_settings_icon) if settings_valid: widget = self.find_child_by_name(setting.name) - widget.setStyleSheet('') - widget.setToolTip('') + if widget is not None: + widget.setStyleSheet('') + widget.setToolTip('') + tab = self.get_tab_index_for_setting_name(setting.name) + self.tab_widget.setTabIcon(tab, self.tab_icons[tab]) export_chosen = False for setting_name, setting in self.settings['export_settings'].items(): @@ -233,21 +357,30 @@ class MainWindow(QtGui.QWidget, CommandBase): if widget is not None: widget.setStyleSheet('QCheckBox{border:3px solid rgba(238, 68, 83, 200); border-radius:5px;}') widget.setToolTip('At least one of these options should be selected.') + tab = self.get_tab_index_for_setting_name(setting.name) + self.tab_widget.setTabIcon(tab, self.warning_settings_icon) else: widget = self.find_child_by_name(setting.name) - widget.setStyleSheet('') - widget.setToolTip('') + if widget is not None: + widget.setStyleSheet('') + widget.setToolTip('') + tab = self.get_tab_index_for_setting_name(setting.name) + self.tab_widget.setTabIcon(tab, self.tab_icons[tab]) - return export_chosen and valid_proj_dirs and settings_valid + return export_chosen and settings_valid def project_dir(self): + return self.project_path if hasattr(self, 'input_line'): return self.input_line.text() return '' def output_dir(self): if hasattr(self, 'output_line'): - return self.output_line.text() + if os.path.isabs(self.output_line.text()): + return self.output_line.text() + else: + return os.path.join(self.project_dir(), self.output_line.text()) return '' def create_download_bar(self): @@ -278,6 +411,7 @@ class MainWindow(QtGui.QWidget, CommandBase): open_export_button.setEnabled(False) open_export_button.setIcon(QtGui.QIcon(os.path.join('files', 'images', 'folder_open.png'))) open_export_button.setToolTip('Open Export Folder') + open_export_button.setStatusTip('Open Export Folder') open_export_button.setMaximumWidth(30) open_export_button.setMaximumHeight(30) @@ -481,45 +615,103 @@ class MainWindow(QtGui.QWidget, CommandBase): # Download the file. self.http_get_id = self.http.get(path, self.out_file) + def create_icon_box(self, name, text): + style = 'width:48px;height:48px;background-color:white;border-radius:5px;border:1px solid rgb(50,50,50);' + icon_label = QtGui.QLabel() + icon_label.setStyleSheet(style) + icon_label.setMaximumWidth(48) + icon_label.setMinimumWidth(48) + icon_label.setMaximumHeight(48) + icon_label.setMinimumHeight(48) + + setattr(self, name, icon_label) + + icon_text = QtGui.QLabel(text) + icon_text.setStyleSheet('font-size:10px;') + icon_text.setAlignment(QtCore.Qt.AlignCenter) + vbox = QVBoxLayout() + vbox.setAlignment(QtCore.Qt.AlignCenter) + vbox.addWidget(icon_label) + vbox.addWidget(icon_text) + vbox.setContentsMargins(0, 0, 0, 0) + + w = QtGui.QWidget() + w.setLayout(vbox) + w.setMaximumWidth(70) + return w + def create_directory_choose(self): - group_box = QtGui.QGroupBox("Choose Your Web Project") + group_box = QtGui.QGroupBox('An awesome web project called:') - input_layout = QtGui.QHBoxLayout() + title_hbox = QHBoxLayout() + title_hbox.setContentsMargins(10, 10, 10, 10) - input_label = QtGui.QLabel('Project Directory:') - self.input_line = QtGui.QLineEdit() - self.input_line.textChanged.connect(self.project_path_changed) - input_button = QtGui.QPushButton('...') - input_button.clicked.connect(self.browse_dir) + win_icon = self.create_icon_box('window_icon', 'Window Icon') + exe_icon = self.create_icon_box('exe_icon', 'Exe Icon') + mac_icon = self.create_icon_box('mac_icon', 'Mac Icon') - input_layout.addWidget(input_label) - input_layout.addWidget(self.input_line) - input_layout.addWidget(input_button) - - output_layout = QtGui.QHBoxLayout() - - output_label = QtGui.QLabel('Output Directory:') - self.output_line = QtGui.QLineEdit() - self.output_line.textChanged.connect(self.project_path_changed) - output_button = QtGui.QPushButton('...') - output_button.clicked.connect(self.browse_out_dir) - - output_layout.addWidget(output_label) - output_layout.addWidget(self.output_line) - output_layout.addWidget(output_button) + self.title_label = QtGui.QLabel('TBD') + self.title_label.setStyleSheet('font-size:20px; font-weight:bold;') + title_hbox.addWidget(self.title_label) + title_hbox.addWidget(QtGui.QLabel()) + title_hbox.addWidget(win_icon) + title_hbox.addWidget(exe_icon) + title_hbox.addWidget(mac_icon) vlayout = QtGui.QVBoxLayout() vlayout.setSpacing(5) vlayout.setContentsMargins(10, 5, 10, 5) - vlayout.addLayout(input_layout) - vlayout.addLayout(output_layout) + vlayout.addLayout(title_hbox) + #vlayout.addLayout(input_layout) + #vlayout.addLayout(output_layout) group_box.setLayout(vlayout) return group_box + def set_window_icon(self): + icon_setting = self.get_setting('icon') + mac_icon_setting = self.get_setting('mac_icon') + exe_icon_setting = self.get_setting('exe_icon') + self.set_icon(icon_setting.value, self.window_icon) + if not mac_icon_setting.value: + self.set_icon(icon_setting.value, self.mac_icon) + if not exe_icon_setting.value: + self.set_icon(icon_setting.value, self.exe_icon) + + def set_exe_icon(self): + icon_setting = self.get_setting('exe_icon') + self.set_icon(icon_setting.value, self.exe_icon) + + def set_mac_icon(self): + icon_setting = self.get_setting('mac_icon') + self.set_icon(icon_setting.value, self.mac_icon) + + def set_icon(self, icon_path, icon): + if icon_path: + icon_path = os.path.join(self.project_dir(), icon_path) + if os.path.exists(icon_path): + if icon_path.endswith('.icns'): + #image = QtGui.QImage.fromData(make_thumbnail(icon_path,48), 'png') + return + image = QtGui.QImage(icon_path) + if image.width() >= image.height(): + image = image.scaledToWidth(48, + QtCore.Qt.SmoothTransformation) + else: + image = image.scaledToHeight(48, + QtCore.Qt.SmoothTransformation) + icon.setPixmap(QtGui.QPixmap.fromImage(image)) + icon.setStyleSheet('') + else: + icon.setPixmap(None) + icon.setStyleSheet(self.icon_style) + else: + icon.setStyleSheet(self.icon_style) + + def call_with_object(self, name, obj, *args, **kwargs): """Allows arguments to be passed to click events""" def call(*cargs, **ckwargs): @@ -544,51 +736,67 @@ class MainWindow(QtGui.QWidget, CommandBase): return self.find_child_by_name('app_name').text() def browse_dir(self): - self.update_json = False - directory = QtGui.QFileDialog.getExistingDirectory(self, "Find Project Directory", - self.project_dir() or QtCore.QDir.currentPath()) + directory = QtGui.QFileDialog.getExistingDirectory(self, 'Find Project Directory', + self.project_dir() or self.last_project_dir) + if directory: - self.reset_settings() - self.input_line.setText(directory) - self.output_line.setText(os.path.join(directory, 'output')) + self.load_project(directory) - proj_name = os.path.basename(directory) + def load_project(self, directory): + self.update_json = False + self.project_path = directory + self.save_recent_project(directory) + self.save_project_path(directory) + self.update_recent_files() + self.reset_settings() + #self.input_line.setText(directory) - setting_input = self.find_child_by_name('main') - files = (glob.glob(os.path.join(directory, 'index.html')) + - glob.glob(os.path.join(directory, 'index.php')) + - glob.glob(os.path.join(directory, 'index.htm'))) - if not setting_input.text(): - if files: - setting_input.setText(files[0].replace(self.project_dir() + os.path.sep, '')) - app_name_input = self.find_child_by_name('app_name') - name_input = self.find_child_by_name('name') - name_setting = self.get_setting('name') - title_input = self.find_child_by_name('title') + proj_name = os.path.basename(directory) + self.title_label.setText(proj_name) - if not name_input.text(): - name_input.setText(name_setting.filter_name(proj_name)) + setting_input = self.find_child_by_name('main') + files = (glob.glob(os.path.join(directory, 'index.html')) + + glob.glob(os.path.join(directory, 'index.php')) + + glob.glob(os.path.join(directory, 'index.htm'))) + if not setting_input.text(): + if files: + setting_input.setText(files[0].replace(self.project_dir() + os.path.sep, '')) - if not app_name_input.text(): - app_name_input.setText(proj_name) + app_name_input = self.find_child_by_name('app_name') + name_input = self.find_child_by_name('name') + name_setting = self.get_setting('name') + title_input = self.find_child_by_name('title') - if not title_input.text(): - title_input.setText(proj_name) + if not name_input.text(): + name_input.setText(name_setting.filter_name(proj_name)) - self.load_package_json() - self.open_export_button.setEnabled(True) - self.update_json = True + if not app_name_input.text(): + app_name_input.setText(proj_name) + + if not title_input.text(): + title_input.setText(proj_name) + + self.load_package_json() + + default_dir = 'output' + export_dir_setting = self.get_setting('export_dir') + default_dir = export_dir_setting.value or default_dir + self.output_line.setText(default_dir) + + self.set_window_icon() + self.open_export_button.setEnabled(True) + self.update_json = True def browse_out_dir(self): self.update_json = False directory = QtGui.QFileDialog.getExistingDirectory(self, "Choose Output Directory", (self.output_line.text() or self.project_dir() or - QtCore.QDir.currentPath())) + self.last_project_dir)) if directory: - self.output_line.setText(directory) self.update_json = True + self.output_line.setText(directory) def get_file(self, obj, text_obj, setting, *args, **kwargs): file_path, _ = QtGui.QFileDialog.getOpenFileName(self, 'Choose File', @@ -612,12 +820,22 @@ class MainWindow(QtGui.QWidget, CommandBase): setting.last_value = folder def create_application_settings(self): - group_box = QtGui.QGroupBox("Application Settings") - vlayout = self.create_layout(self.settings['order']['application_setting_order'], cols=4) + group_box = QtGui.QWidget() + vlayout = self.create_layout(self.settings['order']['application_setting_order'], cols=3) group_box.setLayout(vlayout) return group_box + def create_compression_settings(self): + group_box = QtGui.QWidget() + vlayout = self.create_layout(self.settings['order']['compression_setting_order'], cols=3) + warning_label = QtGui.QLabel('Note: When using compression (greater than 0) it will decrease the executable size,\nbut will increase the startup time when running it.') + vbox = QtGui.QVBoxLayout() + vbox.addLayout(vlayout) + vbox.addWidget(warning_label) + group_box.setLayout(vbox) + return group_box + def create_setting(self, name): setting = self.get_setting(name) if setting.type == 'string': @@ -634,30 +852,50 @@ class MainWindow(QtGui.QWidget, CommandBase): return self.create_range_setting(name) def create_window_settings(self): - group_box = QtGui.QGroupBox("Window Settings") - vlayout = self.create_layout(self.settings['order']['window_setting_order'], cols=4) + group_box = QtGui.QWidget() + vlayout = self.create_layout(self.settings['order']['window_setting_order'], cols=3) group_box.setLayout(vlayout) return group_box def create_export_settings(self): - group_box = QtGui.QGroupBox("Export Settings") + group_box = QtGui.QWidget() vlayout = self.create_layout(self.settings['order']['export_setting_order'], cols=4) - group_box.setLayout(vlayout) + output_layout = QtGui.QHBoxLayout() + + output_label = QtGui.QLabel('Output Directory:') + self.output_line = QtGui.QLineEdit() + self.output_line.textChanged.connect(self.call_with_object('setting_changed', + self.output_line, self.get_setting('export_dir'))) + self.output_line.textChanged.connect(self.project_path_changed) + self.output_line.setStatusTip('The output directory relative to the project directory.') + output_button = QtGui.QPushButton('...') + output_button.clicked.connect(self.browse_out_dir) + + output_layout.addWidget(output_label) + output_layout.addWidget(self.output_line) + output_layout.addWidget(output_button) + vbox = QtGui.QVBoxLayout() + vbox.addLayout(vlayout) + vbox.addLayout(output_layout) + + group_box.setLayout(vbox) return group_box def create_download_settings(self): - group_box = QtGui.QGroupBox("Download Settings") - vlayout = self.create_layout(self.settings['order']['download_setting_order'], 2) + group_box = QtGui.QWidget() + vlayout = self.create_layout(self.settings['order']['download_setting_order'], cols=1) group_box.setLayout(vlayout) return group_box def create_layout(self, settings, cols=3): glayout = QtGui.QGridLayout() - glayout.setContentsMargins(10, 5, 10, 5) + glayout.setContentsMargins(10, 15, 10, 5) + glayout.setAlignment(QtCore.Qt.AlignTop) glayout.setSpacing(10) + glayout.setHorizontalSpacing(20) col = 0 row = 0 @@ -671,6 +909,7 @@ class MainWindow(QtGui.QWidget, CommandBase): display_name += '*' setting_label = QtGui.QLabel(display_name) setting_label.setToolTip(setting.description) + setting_label.setStatusTip(setting.description) glayout.addWidget(setting_label, row, col) glayout.addLayout(self.create_setting(setting_name), row, col+1) @@ -691,6 +930,8 @@ class MainWindow(QtGui.QWidget, CommandBase): text, setting)) if setting.value: text.setText(str(setting.value)) + text.setStatusTip(setting.description) + text.setToolTip(setting.description) hlayout.addWidget(text) @@ -713,6 +954,8 @@ class MainWindow(QtGui.QWidget, CommandBase): if setting.value: text.setText(str(setting.value)) + text.setStatusTip(setting.description) + text.setToolTip(setting.description) text.textChanged.connect(self.call_with_object('setting_changed', text, setting)) @@ -739,6 +982,8 @@ class MainWindow(QtGui.QWidget, CommandBase): if setting.value: text.setText(str(setting.value)) + text.setStatusTip(setting.description) + text.setToolTip(setting.description) text.textChanged.connect(self.call_with_object('setting_changed', text, setting)) @@ -752,6 +997,8 @@ class MainWindow(QtGui.QWidget, CommandBase): for sgroup in self.settings['setting_groups']: for setting in sgroup.values(): widget = self.find_child_by_name(setting.name) + if widget is None: + continue if (setting.type == 'string' or setting.type == 'file' or @@ -813,7 +1060,7 @@ class MainWindow(QtGui.QWidget, CommandBase): if (setting.type == 'string' or setting.type == 'file' or setting.type == 'folder'): - setting.value = obj.text() + setting.value = args[0] elif setting.type == 'check': setting.value = obj.isChecked() check_action = setting.check_action @@ -824,6 +1071,11 @@ class MainWindow(QtGui.QWidget, CommandBase): elif setting.type == 'range': setting.value = obj.value() + if setting.action is not None: + action = getattr(self, setting.action, None) + if callable(action): + action() + if self.update_json: json_file = os.path.join(self.project_dir(), 'package.json') @@ -854,6 +1106,8 @@ class MainWindow(QtGui.QWidget, CommandBase): check.clicked.connect(self.call_with_object('setting_changed', check, setting)) check.setChecked(setting.value) + check.setStatusTip(setting.description) + check.setToolTip(setting.description) hlayout.addWidget(check) @@ -877,6 +1131,9 @@ class MainWindow(QtGui.QWidget, CommandBase): combo.editTextChanged.connect(self.call_with_object('setting_changed', combo, setting)) + combo.setStatusTip(setting.description) + combo.setToolTip(setting.description) + for val in setting.values: combo.addItem(val) @@ -884,6 +1141,7 @@ class MainWindow(QtGui.QWidget, CommandBase): if default_index != -1: combo.setCurrentIndex(default_index) + hlayout.addWidget(QtGui.QLabel()) hlayout.addWidget(combo) if button: hlayout.addWidget(button) @@ -901,12 +1159,14 @@ class MainWindow(QtGui.QWidget, CommandBase): button.clicked.connect(lambda: setting.button_callback(button)) slider = QtGui.QSlider(QtCore.Qt.Orientation.Horizontal) - slider.setRange(0, 9) + slider.setRange(setting.min, setting.max) slider.valueChanged.connect(self.call_with_object('setting_changed', slider, setting)) slider.setObjectName(setting.name) slider.setValue(setting.default_value) + slider.setStatusTip(setting.description) + slider.setToolTip(setting.description) range_label = QtGui.QLabel(str(setting.default_value)) range_label.setMaximumWidth(30) @@ -961,7 +1221,7 @@ if __name__ == '__main__': QCoreApplication.setOrganizationName("SimplyPixelated") QCoreApplication.setOrganizationDomain("simplypixelated.com") - frame = MainWindow(1000, 500, app) + frame = MainWindow(800, 500, app) frame.show_and_raise() sys.exit(app.exec_())