def testPhrase(self): #Test loading of quote.txt resource f = open(adjust_filename('quoteEnUS.txt', __file__), "r") orig = f.read() f.close() f = QFile(':/quote.txt') f.open(QIODevice.ReadOnly) #|QIODevice.Text) print("Error:", f.errorString()) copy = f.readAll() f.close() self.assertEqual(orig, copy)
def OnSaveFilePath(self, filePath): """""" file = QFile(filePath) if not file.open(QFile.WriteOnly | QFile.Text): QMessageBox.warning(self, self.tr('Warning'), self.tr('Cannot write file') + ' %s:\n%s.' % (filePath, file.errorString())) return False outf = QTextStream(file) QApplication.setOverrideCursor(Qt.WaitCursor) outf << self.toPlainText() QApplication.restoreOverrideCursor() self.DoSetCurrentFilePath(filePath) # Clear the Modified Flag. self.document().setModified(False) self.setWindowModified(False) self.setWindowTitle('%s[*]' % self.fileName) return True
def webLoadFinished(self, loaded): print("We loaded a web page") infile = QFile(self) infile.setFileName("ui/resources/jquery-1.9.1.js") if not infile.open(QtCore.QFile.ReadOnly | QtCore.QFile.Text): print("Error opening file: " + infile.errorString()) stream = QtCore.QTextStream(infile) self.jQuery = stream.readAll() infile.close() print("We loaded jQuery") self._web_view.page().mainFrame().evaluateJavaScript(self.jQuery) print("We evaluated jQuery") self._web_view.page().mainFrame().evaluateJavaScript("$( 'div.header' ).css( '-webkit-transition', '-webkit-transform 2s'); $( 'div.header' ).css('-webkit-transform', 'rotate(360deg)')") print("Ran some simple jQuery")
class MainWindow(QtGui.QWidget): base_url = get_base_url() app_settings = {'main': Setting(name='main', display_name='Main file', required=True, type='file', file_types='*.html *.php *.htm'), 'name': Setting(name='name', display_name='App Name', required=True, type='string'), 'description': Setting(name='description', default_default_value='', type='string'), 'version': Setting(name='version', default_value='0.1.0', type='string'), 'keywords':Setting(name='keywords', default_value='', type='string'), 'nodejs': Setting('nodejs', 'Include Nodejs', default_value=True, type='check'), 'node-main': Setting('node-main', 'Alt. Nodejs', default_value='', type='file', file_types='*.js'), 'single-instance': Setting('single-instance', 'Single Instance', default_value=True, type='check')} webkit_settings = {'plugin': Setting('plugin', 'Load plugins', default_value=False, type='check'), 'java': Setting('java', 'Load Java', default_value=False, type='check'), 'page-cache': Setting('page-cache', 'Page Cache', default_value=False, type='check')} window_settings = {'title': Setting(name='title', default_value='', type='string'), 'icon': Setting('icon', 'Window Icon', default_value='', type='file', file_types='*.png *.jpg *.jpeg'), 'width': Setting('width', default_value=640, type='string'), 'height': Setting('height', default_value=480, type='string'), 'min_width': Setting('min_width', default_value=None, type='string'), 'min_height': Setting('min_height', default_value=None, type='string'), 'max_width': Setting('max_width', default_value=None, type='string'), 'max_height': Setting('max_height', default_value=None, type='string'), 'toolbar': Setting('toolbar', 'Show Toolbar', default_value=False, type='check'), 'always-on-top': Setting('always-on-top', 'Keep on top', default_value=False, type='check'), 'frame': Setting('frame', 'Window Frame', default_value=True, type='check'), 'show_in_taskbar': Setting('show_in_taskbar', 'Taskbar', default_value=True, type='check'), 'visible': Setting('visible', default_value=True, type='check'), 'resizable': Setting('resizable', default_value=False, type='check'), 'fullscreen': Setting('fullscreen', default_value=False, type='check'), 'position': Setting('position','Position by', default_value=None, values=[None, 'mouse', 'center'], type='list'), 'as_desktop': Setting('as_desktop', default_value=False, type='check'), } export_settings = {'windows': Setting('windows', default_value=False, type='check', url=base_url+'node-webkit-v{}-win-ia32.zip', extract_files=['nw.exe', 'nw.pak', 'icudt.dll', 'libEGL.dll', 'libGLESv2.dll'], dest_files=['nw.exe', 'nw.pak', 'icudt.dll', 'libEGL.dll', 'libGLESv2.dll']), 'mac': Setting('mac', default_value=False, type='check', url=base_url+'node-webkit-v{}-osx-ia32.zip', extract_file='node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/node-webkit Framework', extract_files=['node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/node-webkit Framework', 'node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Resources/nw.pak'], dest_files=[os.path.join('node-webkit.app','Contents', 'Frameworks','node-webkit Framework.framework', 'node-webkit Framework'), os.path.join('node-webkit.app','Contents', 'Frameworks','node-webkit Framework.framework', 'Resources', 'nw.pak')]), 'linux-x64': Setting('linux-x64', default_value=False, type='check', url=base_url+'node-webkit-v{}-linux-x64.tar.gz', extract_file='node-webkit-v{}-linux-x64/nw', extract_files=['node-webkit-v{}-linux-x64/nw', 'node-webkit-v{}-linux-x64/nw.pak'], dest_files=['nw', 'nw.pak']), 'linux-x64-NewLib': Setting('linux-x64-NewLib', default_value=False, type='check', url=base_url+'node-webkit-v{}-linux-x64.tar.gz', extract_file='node-webkit-v{}-linux-x64/nw', extract_files=['node-webkit-v{}-linux-x64/nw', 'node-webkit-v{}-linux-x64/nw.pak'], dest_files=['nw', 'nw.pak']), 'linux-x32': Setting('linux-x32', default_value=False, type='check', url=base_url+'node-webkit-v{}-linux-ia32.tar.gz', extract_file='node-webkit-v{}-linux-ia32/nw', extract_files=['node-webkit-v{}-linux-ia32/nw', 'node-webkit-v{}-linux-ia32/nw.pak'], dest_files=['nw', 'nw.pak']), 'linux-x32-NewLib': Setting('linux-x32-NewLib', default_value=False, type='check', url=base_url+'node-webkit-v{}-linux-ia32.tar.gz', extract_file='node-webkit-v{}-linux-ia32/nw', extract_files=['node-webkit-v{}-linux-ia32/nw', 'node-webkit-v{}-linux-ia32/nw.pak'], dest_files=['nw', 'nw.pak'])} download_settings = {'nw_version':Setting('nw_version', 'Node-webkit version', default_value='0.9.2', values=[], type='list'), 'force_download': Setting('force_download', default_value=False, type='check')} _setting_groups = [app_settings, webkit_settings, window_settings, export_settings, download_settings] application_setting_order = ['main', 'name', 'node-main', 'description', 'version', 'keywords', 'nodejs', 'single-instance', 'plugin', 'java', 'page-cache'] window_setting_order = ['title', 'icon', 'position', 'width', 'height', 'min_width', 'min_height', 'max_width', 'max_height', 'toolbar', 'always-on-top', 'frame', 'show_in_taskbar', 'visible', 'resizable', 'fullscreen', 'as_desktop'] export_setting_order = ['windows', 'linux-x64', 'linux-x64-NewLib', 'mac', 'linux-x32', 'linux-x32-NewLib'] download_setting_order = ['nw_version', 'force_download'] def __init__(self, width, height, parent=None): super(MainWindow, self).__init__(parent) self.setup_nw_versions() self.httpGetId = 0 self.httpRequestAborted = False self.thread = None self.original_packagejson = {} self.resize(width,height) self.extract_error = None self.create_application_layout() self.option_settings_enabled(False) self.setWindowTitle("Web2Executable") def setup_nw_versions(self): nw_version = self.getSetting('nw_version') try: f = open(os.path.join(CWD, 'files','nw-versions.txt')) for line in f: nw_version.values.append(line.strip()) except IOError: nw_version.values.append(nw_version.default_value) 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.dl_settings_widget = self.createDownloadSettings() 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.addWidget(self.dl_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) self.dl_settings_widget.setEnabled(is_enabled) def export(self, export_button, cancel_button): #self.delete_files_if_forced() self.get_files_to_download() self.try_to_download_files() def delete_files_if_forced(self): forced = self.getSetting('force_download').value if forced: for ex_setting in self.export_settings.values(): for dest_file in ex_setting.dest_files: f_path = os.path.join('files', ex_setting.name, dest_file) if os.path.exists(f_path): os.remove(f_path) def get_files_to_download(self): self.files_to_download = [] for setting_name, setting in self.export_settings.items(): if setting.value == True: self.files_to_download.append(setting) def try_to_download_files(self): if self.files_to_download: self.progress_bar.setVisible(True) self.cancel_button.setEnabled(True) self.disableUIWhileWorking() self.download_file_with_error_handling() 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!') def selected_version(self): return self.getSetting('nw_version').value def download_file_with_error_handling(self): setting = self.files_to_download.pop() try: self.downloadFile(setting.url.format(self.selected_version(), self.selected_version()), setting) except Exception as e: if os.path.exists(setting.save_file_path(self.selected_version())): os.remove(setting.save_file_path(self.selected_version())) error = ''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) self.show_error(error) self.enable_ui_after_error() def enable_ui_after_error(self): self.enableUI() self.progress_text = '' self.progress_bar.setVisible(False) self.cancel_button.setEnabled(False) def show_error(self, exception): QtGui.QMessageBox.information(self, 'Error!', str(exception)) def disableUIWhileWorking(self): self.option_settings_enabled(False) self.directory_chooser_widget.setEnabled(False) def enableUI(self): self.option_settings_enabled(True) self.directory_chooser_widget.setEnabled(True) def requiredSettingsFilled(self): proj_dir = self.projectDir() out_dir = self.outputDir() valid_proj_dir = False if proj_dir and out_dir: if os.path.exists(proj_dir): valid_proj_dirs = True settings_valid = True for sgroup in self._setting_groups: for sname, setting in sgroup.items(): if setting.required and not setting.value: return False if setting.type == 'file' and setting.value and not os.path.exists(os.path.join(self.projectDir(),setting.value)): log(setting.value, "does not exist") settings_valid = False export_chosen = False for setting_name, setting in self.export_settings.items(): if setting.value: export_chosen = True return export_chosen and valid_proj_dirs and settings_valid def projectDir(self): if hasattr(self, 'input_line'): return self.input_line.text() return '' def outputDir(self): if hasattr(self, 'output_line'): return self.output_line.text() return '' def createDownloadBar(self): hlayout = QtGui.QHBoxLayout() vlayout = QtGui.QVBoxLayout() progress_label = QtGui.QLabel('') progress_bar = QtGui.QProgressBar() progress_bar.setVisible(False) vlayout.addWidget(progress_label) vlayout.addWidget(progress_bar) vlayout.addWidget(QtGui.QLabel('')) ex_button = QtGui.QPushButton('Export') ex_button.setEnabled(False) cancel_button = QtGui.QPushButton('Cancel Download') cancel_button.setEnabled(False) ex_button.clicked.connect(self.callWithObject('export', ex_button, cancel_button)) cancel_button.clicked.connect(self.cancelDownload) buttonBox = QtGui.QDialogButtonBox() buttonBox.addButton(cancel_button, QtGui.QDialogButtonBox.RejectRole) buttonBox.addButton(ex_button, QtGui.QDialogButtonBox.AcceptRole) hlayout.addLayout(vlayout) hlayout.addWidget(buttonBox) self.progress_label = progress_label self.progress_bar = progress_bar self.cancel_button = cancel_button http = QHttp(self) http.requestFinished.connect(self.httpRequestFinished) http.dataReadProgress.connect(self.updateProgressBar) http.responseHeaderReceived.connect(self.readResponseHeader) self.http = http self.ex_button = ex_button return hlayout def readResponseHeader(self, response_header): # Check for genuine error conditions. if response_header.statusCode() not in (200, 300, 301, 302, 303, 307): self.show_error('Download failed: {}.'.format(response_header.reasonPhrase())) self.httpRequestAborted = True self.http.abort() self.enable_ui_after_error() def httpRequestFinished(self, requestId, error): if requestId != self.httpGetId: return if self.httpRequestAborted: if self.outFile is not None: self.outFile.close() self.outFile.remove() self.outFile = None return self.outFile.close() if error: self.outFile.remove() self.show_error('Download failed: {}.'.format(self.http.errorString())) self.enable_ui_after_error() self.continueDownloadingOrExtract() def continueDownloadingOrExtract(self): if self.files_to_download: self.progress_bar.setVisible(True) self.cancel_button.setEnabled(True) self.disableUIWhileWorking() self.download_file_with_error_handling() else: self.progress_text = 'Done.' self.cancel_button.setEnabled(False) self.progress_bar.setVisible(False) 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): self.thread = BackgroundThread(self, method_name) self.thread.finished.connect(callback) self.thread.start() def makeOutputFilesInBackground(self): self.ex_button.setEnabled(False) self.runInBackground('makeOutputDirs', self.doneMakingFiles) def doneMakingFiles(self): self.ex_button.setEnabled(self.requiredSettingsFilled()) self.progress_text = 'Done Exporting.' self.enableUI() if self.output_err: self.show_error(self.output_err) self.enable_ui_after_error() def extractFilesInBackground(self): self.progress_text = 'Extracting.' self.ex_button.setEnabled(False) self.runInBackground('extractFiles', self.doneExtracting) def extractFiles(self): self.extract_error = None for setting_name, setting in self.export_settings.items(): save_file_path = setting.save_file_path(self.selected_version()) try: if setting.value: extract_path = os.path.join('files', setting.name) if os.path.exists(save_file_path): for dest_file, fbytes in setting.get_file_bytes(self.selected_version()): with open(os.path.join(extract_path, dest_file), 'wb+') as d: d.write(fbytes) self.progress_text += '.' if os.path.exists(save_file_path): os.remove(save_file_path) #remove the zip/tar since we don't need it anymore self.progress_text += '.' except (tarfile.ReadError, zipfile.BadZipfile) as e: if os.path.exists(save_file_path): os.remove(save_file_path) self.extract_error = e #cannot use GUI in thread to notify user. Save it for later def doneExtracting(self): self.ex_button.setEnabled(self.requiredSettingsFilled()) if self.extract_error: self.progress_text = 'Error extracting.' self.show_error('There were one or more errors with your zip/tar files. They were deleted. Please try to export again.') self.enable_ui_after_error() else: self.progress_text = 'Done extracting.' self.makeOutputFilesInBackground() def cancelDownload(self): self.progress_text = 'Download cancelled.' self.cancel_button.setEnabled(False) self.httpRequestAborted = True self.http.abort() self.enableUI() def updateProgressBar(self, bytesRead, totalBytes): if self.httpRequestAborted: return self.progress_bar.setMaximum(totalBytes) self.progress_bar.setValue(bytesRead) def downloadFile(self, path, setting): self.progress_text = 'Downloading {}'.format(path.replace(self.base_url.format(self.selected_version()),'')) url = QUrl(path) fileInfo = QFileInfo(url.path()) fileName = setting.save_file_path(self.selected_version()) archive_exists = QFile.exists(fileName) dest_files_exist = True for dest_file in setting.dest_files: dest_file_path = os.path.join('files', setting.name, dest_file) dest_files_exist &= QFile.exists(dest_file_path) forced = self.getSetting('force_download').value if (archive_exists or dest_files_exist) and not forced: self.continueDownloadingOrExtract() return #QFile.remove(fileName) self.outFile = QFile(fileName) if not self.outFile.open(QIODevice.WriteOnly): self.show_error('Unable to save the file {}: {}.'.format(fileName, self.outFile.errorString())) self.outFile = None self.enableUI() return mode = QHttp.ConnectionModeHttp port = url.port() if port == -1: port = 0 self.http.setHost(url.host(), mode, port) self.httpRequestAborted = False path = QUrl.toPercentEncoding(url.path(), "!$&'()*+,;=:@/") if path: path = str(path) else: path = '/' # Download the file. self.httpGetId = self.http.get(path, self.outFile) def createDirectoryChoose(self): groupBox = QtGui.QGroupBox("Choose Your Web Project") input_layout = QtGui.QHBoxLayout() input_label = QtGui.QLabel('Project Directory:') self.input_line = QtGui.QLineEdit() self.input_line.textChanged.connect(self.projectPathChanged) input_button = QtGui.QPushButton('...') input_button.clicked.connect(self.browseDir) 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.projectPathChanged) output_button = QtGui.QPushButton('...') output_button.clicked.connect(self.browseOutDir) output_layout.addWidget(output_label) output_layout.addWidget(self.output_line) output_layout.addWidget(output_button) vlayout = QtGui.QVBoxLayout() vlayout.addLayout(input_layout) vlayout.addLayout(output_layout) groupBox.setLayout(vlayout) return groupBox def callWithObject(self, name, obj, *args, **kwargs): """Allows arguments to be passed to click events""" def call(): if hasattr(self, name): func = getattr(self, name) func(obj, *args, **kwargs) return call def findChildByName(self, name): return self.findChild(QtCore.QObject, name) def findAllChildren(self, names): children = [] for child in self.findChildren(QtCore.QObject): if child.objectName() in names: children.append(child) return children def projectName(self): return self.findChildByName('name').text() def browseDir(self): directory = QtGui.QFileDialog.getExistingDirectory(self, "Find Project Directory", self.projectDir() or QtCore.QDir.currentPath()) if directory: self.resetSettings() self.input_line.setText(directory) self.output_line.setText(os.path.join(directory,'output')) proj_name = os.path.basename(directory) setting_input = self.findChildByName('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.projectDir()+os.path.sep,'')) app_name_input = self.findChildByName('name') title_input = self.findChildByName('title') if not app_name_input.text(): app_name_input.setText(proj_name) if not title_input.text(): title_input.setText(proj_name) self.loadPackageJson() def browseOutDir(self): directory = QtGui.QFileDialog.getExistingDirectory(self, "Choose Output Directory", self.projectDir() or QtCore.QDir.currentPath()) if directory: self.output_line.setText(directory) def getFile(self, obj, text_obj, setting, *args, **kwargs): file, junk = QtGui.QFileDialog.getOpenFileName(self, 'Choose File', self.projectDir() or QtCore.QDir.currentPath(), setting.file_types) if file: file = file.replace(self.projectDir()+os.path.sep,'') text_obj.setText(file) def createApplicationSettings(self): groupBox = QtGui.QGroupBox("Application Settings") vlayout = self.createLayout(self.application_setting_order) groupBox.setLayout(vlayout) return groupBox def createSetting(self, name): setting = self.getSetting(name) if setting.type == 'string': return self.createTextInputSetting(name) elif setting.type == 'file': return self.createTextInputWithFileSetting(name) elif setting.type == 'check': return self.createCheckSetting(name) elif setting.type == 'list': return self.createListSetting(name) def createWindowSettings(self): groupBox = QtGui.QGroupBox("Window Settings") vlayout = self.createLayout(self.window_setting_order) groupBox.setLayout(vlayout) return groupBox def createExportSettings(self): groupBox = QtGui.QGroupBox("Export to") vlayout = self.createLayout(self.export_setting_order) groupBox.setLayout(vlayout) return groupBox def createDownloadSettings(self): groupBox = QtGui.QGroupBox("Download Settings") vlayout = self.createLayout(self.download_setting_order) groupBox.setLayout(vlayout) return groupBox def createLayout(self, settings, cols=3): glayout = QtGui.QGridLayout() col = 0 row = 0 for setting_name in settings: setting = self.getSetting(setting_name) if col >= cols*2: row += 1 col = 0 display_name = setting.display_name+':' if setting.required: display_name += '*' glayout.addWidget(QtGui.QLabel(display_name),row,col) glayout.addLayout(self.createSetting(setting_name),row,col+1) col += 2 return glayout def createTextInputSetting(self, name): hlayout = QtGui.QHBoxLayout() setting = self.getSetting(name) text = QtGui.QLineEdit() text.setObjectName(setting.name) text.textChanged.connect(self.callWithObject('settingChanged', text, setting)) if setting.value: text.setText(str(setting.value)) hlayout.addWidget(text) return hlayout def createTextInputWithFileSetting(self, name): hlayout = QtGui.QHBoxLayout() setting = self.getSetting(name) text = QtGui.QLineEdit() text.setObjectName(setting.name) button = QtGui.QPushButton('...') button.setMaximumWidth(30) button.setMaximumHeight(26) button.clicked.connect(self.callWithObject('getFile', button, text, setting)) if setting.value: text.setText(str(setting.value)) text.textChanged.connect(self.callWithObject('settingChanged', text, setting)) hlayout.addWidget(text) hlayout.addWidget(button) return hlayout def resetSettings(self): for sgroup in self._setting_groups: for setting in sgroup.values(): widget = self.findChildByName(setting.name) if setting.type == 'string' or setting.type == 'file': old_val = '' if setting.default_value is not None: old_val = setting.default_value setting.value = old_val widget.setText(str(old_val)) elif setting.type == 'check': old_val = False if setting.default_value is not None: old_val = setting.default_value setting.value = old_val widget.setChecked(old_val) def settingChanged(self, obj, setting, *args, **kwargs): if setting.type == 'string' or setting.type == 'file': setting.value = obj.text() elif setting.type == 'check': setting.value = obj.isChecked() elif setting.type == 'list': setting.value = obj.currentText() self.ex_button.setEnabled(self.requiredSettingsFilled()) def projectPathChanged(self): self.ex_button.setEnabled(self.requiredSettingsFilled()) dirs_filled_out = False if self.projectDir() and self.outputDir(): if os.path.exists(self.projectDir()): dirs_filled_out = True self.option_settings_enabled(dirs_filled_out) def getSetting(self, name): for setting_group in self._setting_groups: if name in setting_group: setting = setting_group[name] return setting def createCheckSetting(self, name): hlayout = QtGui.QHBoxLayout() setting = self.getSetting(name) check = QtGui.QCheckBox() check.setObjectName(setting.name) check.clicked.connect(self.callWithObject('settingChanged', check, setting)) check.setChecked(setting.value) hlayout.addWidget(check) return hlayout def createListSetting(self, name): hlayout = QtGui.QHBoxLayout() setting = self.getSetting(name) combo = QtGui.QComboBox() combo.setObjectName(setting.name) combo.currentIndexChanged.connect(self.callWithObject('settingChanged', combo, setting)) combo.editTextChanged.connect(self.callWithObject('settingChanged', combo, setting)) for val in setting.values: combo.addItem(val) default_index = combo.findData(setting.default_value) if default_index != -1: combo.setCurrentIndex(default_index) hlayout.addWidget(combo) return hlayout def generate_json(self): if 'webkit' not in self.original_packagejson: self.original_packagejson['webkit'] = {} if 'window' not in self.original_packagejson: self.original_packagejson['window'] = {} dic = self.original_packagejson for setting_name, setting in self.app_settings.items(): if setting.value is not None: dic[setting_name] = setting.value if setting_name == 'keywords': dic[setting_name] = re.findall("\w+", setting.value) for setting_name, setting in self.window_settings.items(): if setting.value is not None: if 'height' in setting.name or 'width' in setting.name: try: dic['window'][setting_name] = int(setting.value) except ValueError: pass else: dic['window'][setting_name] = setting.value for setting_name, setting in self.webkit_settings.items(): if setting.value is not None: dic['webkit'][setting_name] = setting.value s = json.dumps(dic, indent=4) return s def loadPackageJson(self): p_json = glob.glob(os.path.join(self.projectDir(), 'package.json')) if p_json: json_str = '' with open(p_json[0], 'r') as f: json_str = f.read() try: self.load_from_json(json_str) except ValueError: #Json file is invalid log( 'Warning: Json file invalid.') def load_from_json(self, json_str): dic = json.loads(json_str) self.original_packagejson = dic stack = [('root',dic)] while stack: parent, new_dic = stack.pop() for item in new_dic: setting_field = self.findChildByName(item) setting = self.getSetting(item) if setting_field: if setting.type == 'file' or setting.type == 'string': val_str = self.convert_val_to_str(new_dic[item]) setting_field.setText(val_str) setting.value = val_str if setting.type == 'check': setting_field.setChecked(new_dic[item]) setting.value = new_dic[item] if isinstance(new_dic[item], dict): stack.append((item,new_dic[item])) def convert_val_to_str(self, val): if isinstance(val, (list,tuple)): return ', '.join(val) return str(val).replace(self.projectDir()+os.path.sep,'') def copyFilesToProjectFolder(self): old_dir = CWD os.chdir(self.projectDir()) for sgroup in self._setting_groups: for setting in sgroup.values(): if setting.type == 'file' and setting.value: try: shutil.copy(setting.value, os.path.join(self.outputDir(), self.projectName())) setting.value = os.path.basename(setting.value) except shutil.Error as e:#same file warning log( 'Warning: {}'.format(e)) os.chdir(old_dir) def makeOutputDirs(self): self.output_err = '' try: self.progress_text = 'Removing old output directory...' outputDir = os.path.join(self.outputDir(), self.projectName()) tempDir = os.path.join(TEMP_DIR, 'webexectemp') if os.path.exists(tempDir): shutil.rmtree(tempDir) self.progress_text = 'Making new directories...' if not os.path.exists(outputDir): os.makedirs(outputDir) os.makedirs(tempDir) self.copyFilesToProjectFolder() json_file = os.path.join(self.projectDir(), 'package.json') with open(json_file, 'w+') as f: f.write(self.generate_json()) zip_file = os.path.join(tempDir, self.projectName()+'.nw') zip_files(zip_file, self.projectDir(), exclude_paths=[outputDir]) for ex_setting in self.export_settings.values(): if ex_setting.value: self.progress_text = 'Making files for {}'.format(ex_setting.display_name) export_dest = os.path.join(outputDir, ex_setting.name) if os.path.exists(export_dest): shutil.rmtree(export_dest) #shutil will make the directory for us shutil.copytree(os.path.join('files', ex_setting.name), export_dest) self.progress_text += '.' if ex_setting.name == 'mac': app_path = os.path.join(export_dest, self.projectName()+'.app') enc_app_path = app_path.encode('utf8') enc_export_dest = export_dest.encode('utf8') shutil.move(os.path.join(export_dest, 'node-webkit.app'), app_path) self.progress_text += '.' shutil.copy(zip_file, os.path.join(app_path, 'Contents', 'Resources', 'app.nw')) self.progress_text += '.' else: ext = '' if ex_setting.name == 'windows': ext = '.exe' nw_path = os.path.join(export_dest, ex_setting.dest_files[0]) dest_binary_path = os.path.join(export_dest, self.projectName()+ext) if ex_setting.name == 'linux-x64-NewLib': command = 'sed -i s/udev.so.0/udev.so.1/g ' + nw_path os.system(command.encode('utf8')) elif ex_setting.name == 'linux-x32-NewLib': command = 'sed -i s/udev.so.0/udev.so.1/g ' + nw_path os.system(command.encode('utf8')) join_files(os.path.join(export_dest, self.projectName()+ext), nw_path, zip_file) sevenfivefive = stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH os.chmod(dest_binary_path, sevenfivefive) self.progress_text += '.' if os.path.exists(nw_path): os.remove(nw_path) except Exception as e: self.output_err += ''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) finally: shutil.rmtree(tempDir) def show_and_raise(self): self.show() self.raise_()
class MainWindow(QtGui.QWidget): base_url = get_base_url() app_settings = {'main': Setting(name='main', display_name='Main file', required=True, type='file', file_types='*.html *.php *.htm'), 'name': Setting(name='name', display_name='App Name', required=True, type='string'), 'description': Setting(name='description', default_default_value='', type='string'), 'version': Setting(name='version', default_value='0.1.0', type='string'), 'keywords':Setting(name='keywords', default_value='', type='string'), 'nodejs': Setting('nodejs', 'Include Nodejs', default_value=True, type='check'), 'node-main': Setting('node-main', 'Alt. Nodejs', default_value='', type='file', file_types='*.js'), 'single-instance': Setting('single-instance', 'Single Instance', default_value=True, type='check')} webkit_settings = {'plugin': Setting('plugin', 'Load plugins', default_value=False, type='check'), 'java': Setting('java', 'Load Java', default_value=False, type='check'), 'page-cache': Setting('page-cache', 'Page Cache', default_value=False, type='check')} window_settings = {'title': Setting(name='title', default_value='', type='string'), 'icon': Setting('icon', 'Window Icon', default_value='', type='file', file_types='*.png *.jpg *.jpeg'), 'width': Setting('width', default_value=640, type='string'), 'height': Setting('height', default_value=480, type='string'), 'min_width': Setting('min_width', default_value=None, type='string'), 'min_height': Setting('min_height', default_value=None, type='string'), 'max_width': Setting('max_width', default_value=None, type='string'), 'max_height': Setting('max_height', default_value=None, type='string'), 'toolbar': Setting('toolbar', 'Show Toolbar', default_value=False, type='check'), 'always-on-top': Setting('always-on-top', 'Keep on top', default_value=False, type='check'), 'frame': Setting('frame', 'Window Frame', default_value=True, type='check'), 'show_in_taskbar': Setting('show_in_taskbar', 'Taskbar', default_value=True, type='check'), 'visible': Setting('visible', default_value=True, type='check'), 'resizable': Setting('resizable', default_value=False, type='check'), 'fullscreen': Setting('fullscreen', default_value=False, type='check'), 'position': Setting('position','Position by', default_value=None, values=[None, 'mouse', 'center'], type='list'), 'as_desktop': Setting('as_desktop', default_value=False, type='check'), } export_settings = {'windows': Setting('windows', default_value=False, type='check', url=base_url+'node-webkit-v{}-win-ia32.zip', extract_files=['nw.exe', 'nw.pak', 'icudt.dll', 'libEGL.dll', 'libGLESv2.dll'], dest_files=['nw.exe', 'nw.pak', 'icudt.dll', 'libEGL.dll', 'libGLESv2.dll']), 'mac': Setting('mac', default_value=False, type='check', url=base_url+'node-webkit-v{}-osx-ia32.zip', extract_file='node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/node-webkit Framework', extract_files=['node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/node-webkit Framework', 'node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Resources/nw.pak'], dest_files=[os.path.join('node-webkit.app','Contents', 'Frameworks','node-webkit Framework.framework', 'node-webkit Framework'), os.path.join('node-webkit.app','Contents', 'Frameworks','node-webkit Framework.framework', 'Resources', 'nw.pak')]), 'linux-x64': Setting('linux-x64', default_value=False, type='check', url=base_url+'node-webkit-v{}-linux-x64.tar.gz', extract_file='node-webkit-v{}-linux-x64/nw', extract_files=['node-webkit-v{}-linux-x64/nw', 'node-webkit-v{}-linux-x64/nw.pak'], dest_files=['nw', 'nw.pak']), 'linux-x32': Setting('linux-x32', default_value=False, type='check', url=base_url+'node-webkit-v{}-linux-ia32.tar.gz', extract_file='node-webkit-v{}-linux-ia32/nw', extract_files=['node-webkit-v{}-linux-ia32/nw', 'node-webkit-v{}-linux-ia32/nw.pak'], dest_files=['nw', 'nw.pak'])} download_settings = {'nw_version':Setting('nw_version', 'Node-webkit version', default_value='0.9.2', values=[], type='list'), 'force_download': Setting('force_download', default_value=False, type='check')} _setting_groups = [app_settings, webkit_settings, window_settings, export_settings, download_settings] application_setting_order = ['main', 'name', 'node-main', 'description', 'version', 'keywords', 'nodejs', 'single-instance', 'plugin', 'java', 'page-cache'] window_setting_order = ['title', 'icon', 'position', 'width', 'height', 'min_width', 'min_height', 'max_width', 'max_height', 'toolbar', 'always-on-top', 'frame', 'show_in_taskbar', 'visible', 'resizable', 'fullscreen', 'as_desktop'] export_setting_order = ['windows', 'linux-x64', 'mac', 'linux-x32'] download_setting_order = ['nw_version', 'force_download'] def __init__(self, width, height, parent=None): super(MainWindow, self).__init__(parent) self.setup_nw_versions() self.httpGetId = 0 self.httpRequestAborted = False self.thread = None self.original_packagejson = {} self.resize(width,height) self.extract_error = None self.create_application_layout() self.option_settings_enabled(False) self.setWindowTitle("Web2Executable") def setup_nw_versions(self): nw_version = self.getSetting('nw_version') try: f = open(os.path.join(CWD, 'files','nw-versions.txt')) for line in f: nw_version.values.append(line.strip()) except IOError: nw_version.values.append(nw_version.default_value) 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.dl_settings_widget = self.createDownloadSettings() 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.addWidget(self.dl_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) self.dl_settings_widget.setEnabled(is_enabled) def export(self, export_button, cancel_button): #self.delete_files_if_forced() self.get_files_to_download() self.try_to_download_files() def delete_files_if_forced(self): forced = self.getSetting('force_download').value if forced: for ex_setting in self.export_settings.values(): for dest_file in ex_setting.dest_files: f_path = os.path.join('files', ex_setting.name, dest_file) if os.path.exists(f_path): os.remove(f_path) def get_files_to_download(self): self.files_to_download = [] for setting_name, setting in self.export_settings.items(): if setting.value == True: self.files_to_download.append(setting) def try_to_download_files(self): if self.files_to_download: self.progress_bar.setVisible(True) self.cancel_button.setEnabled(True) self.disableUIWhileWorking() self.download_file_with_error_handling() 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!') def selected_version(self): return self.getSetting('nw_version').value def download_file_with_error_handling(self): setting = self.files_to_download.pop() try: self.downloadFile(setting.url.format(self.selected_version(), self.selected_version()), setting) except Exception as e: if os.path.exists(setting.save_file_path(self.selected_version())): os.remove(setting.save_file_path(self.selected_version())) error = ''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) self.show_error(error) self.enable_ui_after_error() def enable_ui_after_error(self): self.enableUI() self.progress_text = '' self.progress_bar.setVisible(False) self.cancel_button.setEnabled(False) def show_error(self, exception): QtGui.QMessageBox.information(self, 'Error!', str(exception)) def disableUIWhileWorking(self): self.option_settings_enabled(False) self.directory_chooser_widget.setEnabled(False) def enableUI(self): self.option_settings_enabled(True) self.directory_chooser_widget.setEnabled(True) def requiredSettingsFilled(self): proj_dir = self.projectDir() out_dir = self.outputDir() valid_proj_dir = False if proj_dir and out_dir: if os.path.exists(proj_dir): valid_proj_dirs = True settings_valid = True for sgroup in self._setting_groups: for sname, setting in sgroup.items(): if setting.required and not setting.value: return False if setting.type == 'file' and setting.value and not os.path.exists(os.path.join(self.projectDir(),setting.value)): log(setting.value, "does not exist") settings_valid = False export_chosen = False for setting_name, setting in self.export_settings.items(): if setting.value: export_chosen = True return export_chosen and valid_proj_dirs and settings_valid def projectDir(self): if hasattr(self, 'input_line'): return self.input_line.text() return '' def outputDir(self): if hasattr(self, 'output_line'): return self.output_line.text() return '' def createDownloadBar(self): hlayout = QtGui.QHBoxLayout() vlayout = QtGui.QVBoxLayout() progress_label = QtGui.QLabel('') progress_bar = QtGui.QProgressBar() progress_bar.setVisible(False) vlayout.addWidget(progress_label) vlayout.addWidget(progress_bar) vlayout.addWidget(QtGui.QLabel('')) ex_button = QtGui.QPushButton('Export') ex_button.setEnabled(False) cancel_button = QtGui.QPushButton('Cancel Download') cancel_button.setEnabled(False) ex_button.clicked.connect(self.callWithObject('export', ex_button, cancel_button)) cancel_button.clicked.connect(self.cancelDownload) buttonBox = QtGui.QDialogButtonBox() buttonBox.addButton(cancel_button, QtGui.QDialogButtonBox.RejectRole) buttonBox.addButton(ex_button, QtGui.QDialogButtonBox.AcceptRole) hlayout.addLayout(vlayout) hlayout.addWidget(buttonBox) self.progress_label = progress_label self.progress_bar = progress_bar self.cancel_button = cancel_button http = QHttp(self) http.requestFinished.connect(self.httpRequestFinished) http.dataReadProgress.connect(self.updateProgressBar) http.responseHeaderReceived.connect(self.readResponseHeader) self.http = http self.ex_button = ex_button return hlayout def readResponseHeader(self, response_header): # Check for genuine error conditions. if response_header.statusCode() not in (200, 300, 301, 302, 303, 307): self.show_error('Download failed: {}.'.format(response_header.reasonPhrase())) self.httpRequestAborted = True self.http.abort() self.enable_ui_after_error() def httpRequestFinished(self, requestId, error): if requestId != self.httpGetId: return if self.httpRequestAborted: if self.outFile is not None: self.outFile.close() self.outFile.remove() self.outFile = None return self.outFile.close() if error: self.outFile.remove() self.show_error('Download failed: {}.'.format(self.http.errorString())) self.enable_ui_after_error() self.continueDownloadingOrExtract() def continueDownloadingOrExtract(self): if self.files_to_download: self.progress_bar.setVisible(True) self.cancel_button.setEnabled(True) self.disableUIWhileWorking() self.download_file_with_error_handling() else: self.progress_text = 'Done.' self.cancel_button.setEnabled(False) self.progress_bar.setVisible(False) 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): self.thread = BackgroundThread(self, method_name) self.thread.finished.connect(callback) self.thread.start() def makeOutputFilesInBackground(self): self.ex_button.setEnabled(False) self.runInBackground('makeOutputDirs', self.doneMakingFiles) def doneMakingFiles(self): self.ex_button.setEnabled(self.requiredSettingsFilled()) self.progress_text = 'Done Exporting.' self.enableUI() if self.output_err: self.show_error(self.output_err) self.enable_ui_after_error() def extractFilesInBackground(self): self.progress_text = 'Extracting.' self.ex_button.setEnabled(False) self.runInBackground('extractFiles', self.doneExtracting) def extractFiles(self): self.extract_error = None for setting_name, setting in self.export_settings.items(): save_file_path = setting.save_file_path(self.selected_version()) try: if setting.value: extract_path = os.path.join('files', setting.name) if os.path.exists(save_file_path): for dest_file, fbytes in setting.get_file_bytes(self.selected_version()): with open(os.path.join(extract_path, dest_file), 'wb+') as d: d.write(fbytes) self.progress_text += '.' if os.path.exists(save_file_path): os.remove(save_file_path) #remove the zip/tar since we don't need it anymore self.progress_text += '.' except (tarfile.ReadError, zipfile.BadZipfile) as e: if os.path.exists(save_file_path): os.remove(save_file_path) self.extract_error = e #cannot use GUI in thread to notify user. Save it for later def doneExtracting(self): self.ex_button.setEnabled(self.requiredSettingsFilled()) if self.extract_error: self.progress_text = 'Error extracting.' self.show_error('There were one or more errors with your zip/tar files. They were deleted. Please try to export again.') self.enable_ui_after_error() else: self.progress_text = 'Done extracting.' self.makeOutputFilesInBackground() def cancelDownload(self): self.progress_text = 'Download cancelled.' self.cancel_button.setEnabled(False) self.httpRequestAborted = True self.http.abort() self.enableUI() def updateProgressBar(self, bytesRead, totalBytes): if self.httpRequestAborted: return self.progress_bar.setMaximum(totalBytes) self.progress_bar.setValue(bytesRead) def downloadFile(self, path, setting): self.progress_text = 'Downloading {}'.format(path.replace(self.base_url.format(self.selected_version()),'')) url = QUrl(path) fileInfo = QFileInfo(url.path()) fileName = setting.save_file_path(self.selected_version()) archive_exists = QFile.exists(fileName) dest_files_exist = True for dest_file in setting.dest_files: dest_file_path = os.path.join('files', setting.name, dest_file) dest_files_exist &= QFile.exists(dest_file_path) forced = self.getSetting('force_download').value if (archive_exists or dest_files_exist) and not forced: self.continueDownloadingOrExtract() return #QFile.remove(fileName) self.outFile = QFile(fileName) if not self.outFile.open(QIODevice.WriteOnly): self.show_error('Unable to save the file {}: {}.'.format(fileName, self.outFile.errorString())) self.outFile = None self.enableUI() return mode = QHttp.ConnectionModeHttp port = url.port() if port == -1: port = 0 self.http.setHost(url.host(), mode, port) self.httpRequestAborted = False path = QUrl.toPercentEncoding(url.path(), "!$&'()*+,;=:@/") if path: path = str(path) else: path = '/' # Download the file. self.httpGetId = self.http.get(path, self.outFile) def createDirectoryChoose(self): groupBox = QtGui.QGroupBox("Choose Your Web Project") input_layout = QtGui.QHBoxLayout() input_label = QtGui.QLabel('Project Directory:') self.input_line = QtGui.QLineEdit() self.input_line.textChanged.connect(self.projectPathChanged) input_button = QtGui.QPushButton('...') input_button.clicked.connect(self.browseDir) 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.projectPathChanged) output_button = QtGui.QPushButton('...') output_button.clicked.connect(self.browseOutDir) output_layout.addWidget(output_label) output_layout.addWidget(self.output_line) output_layout.addWidget(output_button) vlayout = QtGui.QVBoxLayout() vlayout.addLayout(input_layout) vlayout.addLayout(output_layout) groupBox.setLayout(vlayout) return groupBox def callWithObject(self, name, obj, *args, **kwargs): """Allows arguments to be passed to click events""" def call(): if hasattr(self, name): func = getattr(self, name) func(obj, *args, **kwargs) return call def findChildByName(self, name): return self.findChild(QtCore.QObject, name) def findAllChildren(self, names): children = [] for child in self.findChildren(QtCore.QObject): if child.objectName() in names: children.append(child) return children def projectName(self): return self.findChildByName('name').text() def browseDir(self): directory = QtGui.QFileDialog.getExistingDirectory(self, "Find Project Directory", self.projectDir() or QtCore.QDir.currentPath()) if directory: self.resetSettings() self.input_line.setText(directory) self.output_line.setText(os.path.join(directory,'output')) proj_name = os.path.basename(directory) setting_input = self.findChildByName('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.projectDir()+os.path.sep,'')) app_name_input = self.findChildByName('name') title_input = self.findChildByName('title') if not app_name_input.text(): app_name_input.setText(proj_name) if not title_input.text(): title_input.setText(proj_name) self.loadPackageJson() def browseOutDir(self): directory = QtGui.QFileDialog.getExistingDirectory(self, "Choose Output Directory", self.projectDir() or QtCore.QDir.currentPath()) if directory: self.output_line.setText(directory) def getFile(self, obj, text_obj, setting, *args, **kwargs): file, junk = QtGui.QFileDialog.getOpenFileName(self, 'Choose File', self.projectDir() or QtCore.QDir.currentPath(), setting.file_types) if file: file = file.replace(self.projectDir()+os.path.sep,'') text_obj.setText(file) def createApplicationSettings(self): groupBox = QtGui.QGroupBox("Application Settings") vlayout = self.createLayout(self.application_setting_order) groupBox.setLayout(vlayout) return groupBox def createSetting(self, name): setting = self.getSetting(name) if setting.type == 'string': return self.createTextInputSetting(name) elif setting.type == 'file': return self.createTextInputWithFileSetting(name) elif setting.type == 'check': return self.createCheckSetting(name) elif setting.type == 'list': return self.createListSetting(name) def createWindowSettings(self): groupBox = QtGui.QGroupBox("Window Settings") vlayout = self.createLayout(self.window_setting_order) groupBox.setLayout(vlayout) return groupBox def createExportSettings(self): groupBox = QtGui.QGroupBox("Export to") vlayout = self.createLayout(self.export_setting_order) groupBox.setLayout(vlayout) return groupBox def createDownloadSettings(self): groupBox = QtGui.QGroupBox("Download Settings") vlayout = self.createLayout(self.download_setting_order) groupBox.setLayout(vlayout) return groupBox def createLayout(self, settings, cols=3): glayout = QtGui.QGridLayout() col = 0 row = 0 for setting_name in settings: setting = self.getSetting(setting_name) if col >= cols*2: row += 1 col = 0 display_name = setting.display_name+':' if setting.required: display_name += '*' glayout.addWidget(QtGui.QLabel(display_name),row,col) glayout.addLayout(self.createSetting(setting_name),row,col+1) col += 2 return glayout def createTextInputSetting(self, name): hlayout = QtGui.QHBoxLayout() setting = self.getSetting(name) text = QtGui.QLineEdit() text.setObjectName(setting.name) text.textChanged.connect(self.callWithObject('settingChanged', text, setting)) if setting.value: text.setText(str(setting.value)) hlayout.addWidget(text) return hlayout def createTextInputWithFileSetting(self, name): hlayout = QtGui.QHBoxLayout() setting = self.getSetting(name) text = QtGui.QLineEdit() text.setObjectName(setting.name) button = QtGui.QPushButton('...') button.setMaximumWidth(30) button.setMaximumHeight(26) button.clicked.connect(self.callWithObject('getFile', button, text, setting)) if setting.value: text.setText(str(setting.value)) text.textChanged.connect(self.callWithObject('settingChanged', text, setting)) hlayout.addWidget(text) hlayout.addWidget(button) return hlayout def resetSettings(self): for sgroup in self._setting_groups: for setting in sgroup.values(): widget = self.findChildByName(setting.name) if setting.type == 'string' or setting.type == 'file': old_val = '' if setting.default_value is not None: old_val = setting.default_value setting.value = old_val widget.setText(str(old_val)) elif setting.type == 'check': old_val = False if setting.default_value is not None: old_val = setting.default_value setting.value = old_val widget.setChecked(old_val) def settingChanged(self, obj, setting, *args, **kwargs): if setting.type == 'string' or setting.type == 'file': setting.value = obj.text() elif setting.type == 'check': setting.value = obj.isChecked() elif setting.type == 'list': setting.value = obj.currentText() self.ex_button.setEnabled(self.requiredSettingsFilled()) def projectPathChanged(self): self.ex_button.setEnabled(self.requiredSettingsFilled()) dirs_filled_out = False if self.projectDir() and self.outputDir(): if os.path.exists(self.projectDir()): dirs_filled_out = True self.option_settings_enabled(dirs_filled_out) def getSetting(self, name): for setting_group in self._setting_groups: if name in setting_group: setting = setting_group[name] return setting def createCheckSetting(self, name): hlayout = QtGui.QHBoxLayout() setting = self.getSetting(name) check = QtGui.QCheckBox() check.setObjectName(setting.name) check.clicked.connect(self.callWithObject('settingChanged', check, setting)) check.setChecked(setting.value) hlayout.addWidget(check) return hlayout def createListSetting(self, name): hlayout = QtGui.QHBoxLayout() setting = self.getSetting(name) combo = QtGui.QComboBox() combo.setObjectName(setting.name) combo.currentIndexChanged.connect(self.callWithObject('settingChanged', combo, setting)) combo.editTextChanged.connect(self.callWithObject('settingChanged', combo, setting)) for val in setting.values: combo.addItem(val) default_index = combo.findData(setting.default_value) if default_index != -1: combo.setCurrentIndex(default_index) hlayout.addWidget(combo) return hlayout def generate_json(self): if 'webkit' not in self.original_packagejson: self.original_packagejson['webkit'] = {} if 'window' not in self.original_packagejson: self.original_packagejson['window'] = {} dic = self.original_packagejson for setting_name, setting in self.app_settings.items(): if setting.value is not None: dic[setting_name] = setting.value if setting_name == 'keywords': dic[setting_name] = re.findall("\w+", setting.value) for setting_name, setting in self.window_settings.items(): if setting.value is not None: if 'height' in setting.name or 'width' in setting.name: try: dic['window'][setting_name] = int(setting.value) except ValueError: pass else: dic['window'][setting_name] = setting.value for setting_name, setting in self.webkit_settings.items(): if setting.value is not None: dic['webkit'][setting_name] = setting.value s = json.dumps(dic, indent=4) return s def loadPackageJson(self): p_json = glob.glob(os.path.join(self.projectDir(), 'package.json')) if p_json: json_str = '' with open(p_json[0], 'r') as f: json_str = f.read() try: self.load_from_json(json_str) except ValueError: #Json file is invalid log( 'Warning: Json file invalid.') def load_from_json(self, json_str): dic = json.loads(json_str) self.original_packagejson = dic stack = [('root',dic)] while stack: parent, new_dic = stack.pop() for item in new_dic: setting_field = self.findChildByName(item) setting = self.getSetting(item) if setting_field: if setting.type == 'file' or setting.type == 'string': val_str = self.convert_val_to_str(new_dic[item]) setting_field.setText(val_str) setting.value = val_str if setting.type == 'check': setting_field.setChecked(new_dic[item]) setting.value = new_dic[item] if isinstance(new_dic[item], dict): stack.append((item,new_dic[item])) def convert_val_to_str(self, val): if isinstance(val, (list,tuple)): return ', '.join(val) return str(val).replace(self.projectDir()+os.path.sep,'') def copyFilesToProjectFolder(self): old_dir = CWD os.chdir(self.projectDir()) for sgroup in self._setting_groups: for setting in sgroup.values(): if setting.type == 'file' and setting.value: try: shutil.copy(setting.value, self.projectDir()) setting.value = os.path.basename(setting.value) except shutil.Error as e:#same file warning log( 'Warning: {}'.format(e)) os.chdir(old_dir) def makeOutputDirs(self): self.output_err = '' try: self.progress_text = 'Removing old output directory...' outputDir = os.path.join(self.outputDir(), self.projectName()) tempDir = os.path.join(TEMP_DIR, 'webexectemp') if os.path.exists(tempDir): shutil.rmtree(tempDir) self.progress_text = 'Making new directories...' if not os.path.exists(outputDir): os.makedirs(outputDir) os.makedirs(tempDir) self.copyFilesToProjectFolder() json_file = os.path.join(self.projectDir(), 'package.json') with open(json_file, 'w+') as f: f.write(self.generate_json()) zip_file = os.path.join(tempDir, self.projectName()+'.nw') zip_files(zip_file, self.projectDir(), exclude_paths=[outputDir]) for ex_setting in self.export_settings.values(): if ex_setting.value: self.progress_text = 'Making files for {}'.format(ex_setting.display_name) export_dest = os.path.join(outputDir, ex_setting.name) if os.path.exists(export_dest): shutil.rmtree(export_dest) #shutil will make the directory for us shutil.copytree(os.path.join('files', ex_setting.name), export_dest) self.progress_text += '.' if ex_setting.name == 'mac': app_path = os.path.join(export_dest, self.projectName()+'.app') shutil.move(os.path.join(export_dest, 'node-webkit.app'), app_path) self.progress_text += '.' shutil.copy(zip_file, os.path.join(app_path, 'Contents', 'Resources', 'app.nw')) self.progress_text += '.' else: ext = '' if ex_setting.name == 'windows': ext = '.exe' nw_path = os.path.join(export_dest, ex_setting.dest_files[0]) dest_binary_path = os.path.join(export_dest, self.projectName()+ext) join_files(os.path.join(export_dest, self.projectName()+ext), nw_path, zip_file) sevenfivefive = stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH os.chmod(dest_binary_path, sevenfivefive) self.progress_text += '.' if os.path.exists(nw_path): os.remove(nw_path) except Exception as e: self.output_err += ''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) finally: shutil.rmtree(tempDir) def show_and_raise(self): self.show() self.raise_()
def loadFile(self, fileName): """ TOWRITE :param `fileName`: TOWRITE :type `fileName`: QString :rtype: bool """ qDebug("MdiWindow loadFile()") tmpColor = self.getCurrentColor() # QRgb file = QFile(fileName) if not file.open(QFile.ReadOnly | QFile.Text): QMessageBox.warning( self, self.tr("Error reading file"), self.tr("Cannot read file %s:\n%s." % (fileName, file.errorString())) ) return False QApplication.setOverrideCursor(Qt.WaitCursor) ext = self.fileExtension(fileName) # QString qDebug("ext: %s" % qPrintable(ext)) # Read p = embPattern_create() # EmbPattern* if not p: print("Could not allocate memory for embroidery pattern\n") exit(1) readSuccessful = 0 # int ## QString readError reader = embReaderWriter_getByFileName(qPrintable(fileName)) # EmbReaderWriter* if not reader: readSuccessful = 0 readError = "Unsupported read file type: " + fileName qDebug("Unsupported read file type: %s\n" % qPrintable(fileName)) else: readSuccessful = reader.reader(p, qPrintable(fileName)) if not readSuccessful: readError = "Reading file was unsuccessful: " + fileName qDebug("Reading file was unsuccessful: %s\n" % qPrintable(fileName)) ## free(reader) #TODO/REMOVE# not needed in python if not readSuccessful: QMessageBox.warning(self, self.tr("Error reading pattern"), self.tr(qPrintable(readError))) if readSuccessful: embPattern_moveStitchListToPolylines(p) # TODO: Test more stitchCount = embStitchList_count(p.stitchList) # int path = QPainterPath() if p.circleObjList: curCircleObj = p.circleObjList # EmbCircleObjectList* while curCircleObj: c = curCircleObj.circleObj.circle # EmbCircle thisColor = curCircleObj.circleObj.color # EmbColor self.setCurrentColor(qRgb(thisColor.r, thisColor.g, thisColor.b)) # NOTE: With natives, the Y+ is up and libembroidery Y+ is up, so inverting the Y is NOT needed. self.mainWin.nativeAddCircle(embCircle_centerX(c), embCircle_centerY(c), embCircle_radius(c), False, OBJ_RUBBER_OFF) # TODO: fill curCircleObj = curCircleObj.next if p.ellipseObjList: curEllipseObj = p.ellipseObjList # EmbEllipseObjectList* while curEllipseObj: e = curEllipseObj.ellipseObj.ellipse # EmbEllipse thisColor = curEllipseObj.ellipseObj.color # EmbColor self.setCurrentColor(qRgb(thisColor.r, thisColor.g, thisColor.b)) # NOTE: With natives, the Y+ is up and libembroidery Y+ is up, so inverting the Y is NOT needed. self.mainWin.nativeAddEllipse(embEllipse_centerX(e), embEllipse_centerY(e), embEllipse_width(e), embEllipse_height(e), 0, False, OBJ_RUBBER_OFF) # TODO: rotation and fill curEllipseObj = curEllipseObj.next if p.lineObjList: curLineObj = p.lineObjList # EmbLineObjectList* while curLineObj: li = curLineObj.lineObj.line # EmbLine thisColor = curLineObj.lineObj.color # EmbColor self.setCurrentColor(qRgb(thisColor.r, thisColor.g, thisColor.b)) # NOTE: With natives, the Y+ is up and libembroidery Y+ is up, so inverting the Y is NOT needed. self.mainWin.nativeAddLine(embLine_x1(li), embLine_y1(li), embLine_x2(li), embLine_y2(li), 0, OBJ_RUBBER_OFF) # TODO: rotation curLineObj = curLineObj.next if p.pathObjList: # TODO: This is unfinished. It needs more work curPathObjList = p.pathObjList # EmbPathObjectList* while curPathObjList: pathPath = QPainterPath() curPointList = curPathObjList.pathObj.pointList # EmbPointList* thisColor = curPathObjList.pathObj.color # EmbColor if curPointList: pp = curPointList.point # EmbPoint pathPath.moveTo(embPoint_x(pp), -embPoint_y(pp)) # NOTE: Qt Y+ is down and libembroidery Y+ is up, so inverting the Y is needed. curPointList = curPointList.next while curPointList: pp = curPointList.point # EmbPoint pathPath.lineTo(embPoint_x(pp), -embPoint_y(pp)) # NOTE: Qt Y+ is down and libembroidery Y+ is up, so inverting the Y is needed. curPointList = curPointList.next loadPen = QPen(qRgb(thisColor.r, thisColor.g, thisColor.b)) loadPen.setWidthF(0.35) loadPen.setCapStyle(Qt.RoundCap) loadPen.setJoinStyle(Qt.RoundJoin) obj = PathObject(0, 0, pathPath, loadPen.color().rgb()) # PathObject* obj.setObjectRubberMode(OBJ_RUBBER_OFF) self.gscene.addItem(obj) curPathObjList = curPathObjList.next if p.pointObjList: curPointObj = p.pointObjList # EmbPointObjectList* while curPointObj: po = curPointObj.pointObj.point # EmbPoint thisColor = curPointObj.pointObj.color # EmbColor self.setCurrentColor(qRgb(thisColor.r, thisColor.g, thisColor.b)) # NOTE: With natives, the Y+ is up and libembroidery Y+ is up, so inverting the Y is NOT needed. self.mainWin.nativeAddPoint(embPoint_x(po), embPoint_y(po)) curPointObj = curPointObj.next if p.polygonObjList: curPolygonObjList = p.polygonObjList # EmbPolygonObjectList* while curPolygonObjList: polygonPath = QPainterPath() firstPoint = False # bool startX = 0; startY = 0 # qreal x = 0; y = 0 # qreal curPointList = curPolygonObjList.polygonObj.pointList # EmbPointList* thisColor = curPolygonObjList.polygonObj.color # EmbColor self.setCurrentColor(qRgb(thisColor.r, thisColor.g, thisColor.b)) while curPointList: pp = curPointList.point # EmbPoint x = embPoint_x(pp) y = -embPoint_y(pp) # NOTE: Qt Y+ is down and libembroidery Y+ is up, so inverting the Y is needed. if firstPoint: polygonPath.lineTo(x,y) else: polygonPath.moveTo(x,y) firstPoint = True startX = x startY = y curPointList = curPointList.next polygonPath.translate(-startX, -startY) self.mainWin.nativeAddPolygon(startX, startY, polygonPath, OBJ_RUBBER_OFF) curPolygonObjList = curPolygonObjList.next # NOTE: Polylines should only contain NORMAL stitches. if p.polylineObjList: curPolylineObjList = p.polylineObjList # EmbPolylineObjectList* while curPolylineObjList: polylinePath = QPainterPath() firstPoint = False # bool startX = 0; startY = 0 # qreal x = 0; y = 0 # qreal curPointList = curPolylineObjList.polylineObj.pointList # EmbPointList* thisColor = curPolylineObjList.polylineObj.color # EmbColor self.setCurrentColor(qRgb(thisColor.r, thisColor.g, thisColor.b)) while curPointList: pp = curPointList.point # EmbPoint x = embPoint_x(pp) y = -embPoint_y(pp) # NOTE: Qt Y+ is down and libembroidery Y+ is up, so inverting the Y is needed. if firstPoint: polylinePath.lineTo(x,y) else: polylinePath.moveTo(x,y) firstPoint = True startX = x startY = y curPointList = curPointList.next polylinePath.translate(-startX, -startY) self.mainWin.nativeAddPolyline(startX, startY, polylinePath, OBJ_RUBBER_OFF) curPolylineObjList = curPolylineObjList.next if p.rectObjList: curRectObj = p.rectObjList # EmbRectObjectList* while curRectObj: r = curRectObj.rectObj.rect # EmbRect thisColor = curRectObj.rectObj.color # EmbColor self.setCurrentColor(qRgb(thisColor.r, thisColor.g, thisColor.b)) # NOTE: With natives, the Y+ is up and libembroidery Y+ is up, so inverting the Y is NOT needed. self.mainWin.nativeAddRectangle(embRect_x(r), embRect_y(r), embRect_width(r), embRect_height(r), 0, False, OBJ_RUBBER_OFF) # TODO: rotation and fill curRectObj = curRectObj.next self.setCurrentFile(fileName) self.mainWin.statusbar.showMessage("File loaded.") stitches = str(stitchCount) # QString: stitches.setNum(stitchCount) if self.mainWin.getSettingsGridLoadFromFile(): # TODO: Josh, provide me a hoop size and/or grid spacing from the pattern. pass QApplication.restoreOverrideCursor() else: #TODO/PORT# warning shown twice?! redundant ?! QApplication.restoreOverrideCursor() QMessageBox.warning(self, self.tr("Error reading pattern"), self.tr("Cannot read pattern")) ## embPattern_free(p) #TODO/REMOVE# not needed in python # Clear the undo stack so it is not possible to undo past this point. self.gview.getUndoStack().clear() self.setCurrentColor(tmpColor) fileWasLoaded = True self.mainWin.setUndoCleanIcon(fileWasLoaded) return fileWasLoaded