예제 #1
0
    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
예제 #3
0
    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
예제 #4
0
    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")
예제 #5
0
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_()
예제 #6
0
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_()
예제 #7
0
    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