Wed, 17 Jan 2018 16:25:59 +0100
Continued implementing the PyInstaller interface.
--- a/PluginPyInstaller.e4p Tue Jan 16 15:36:57 2018 +0100 +++ b/PluginPyInstaller.e4p Wed Jan 17 16:25:59 2018 +0100 @@ -15,9 +15,15 @@ <Eol index="1"/> <Sources> <Source>PluginPyInstaller.py</Source> + <Source>PyInstaller/PyInstallerCleanupDialog.py</Source> + <Source>PyInstaller/PyInstallerConfigDialog.py</Source> <Source>PyInstaller/__init__.py</Source> <Source>__init__.py</Source> </Sources> + <Forms> + <Form>PyInstaller/PyInstallerCleanupDialog.ui</Form> + <Form>PyInstaller/PyInstallerConfigDialog.ui</Form> + </Forms> <Others> <Other>.hgignore</Other> <Other>ChangeLog</Other> @@ -145,5 +151,6 @@ <FiletypeAssociation pattern="*.ui" type="FORMS"/> <FiletypeAssociation pattern="README" type="OTHERS"/> <FiletypeAssociation pattern="README.*" type="OTHERS"/> + <FiletypeAssociation pattern="Ui_*.py" type="__IGNORE__"/> </FiletypeAssociations> </Project>
--- a/PluginPyInstaller.py Tue Jan 16 15:36:57 2018 +0100 +++ b/PluginPyInstaller.py Wed Jan 17 16:25:59 2018 +0100 @@ -11,8 +11,14 @@ import os import platform +import shutil -from PyQt5.QtCore import QObject, QCoreApplication +from PyQt5.QtCore import pyqtSlot, QObject, QCoreApplication, QTranslator +from PyQt5.QtWidgets import QDialog + +from E5Gui import E5MessageBox +from E5Gui.E5Action import E5Action +from E5Gui.E5Application import e5App import Utilities @@ -154,10 +160,7 @@ # # Linux, Unix ... pyinstallerScripts = ['pyinstaller', 'pyi-makespec'] -## scriptSuffixes = [""] -## for minorVersion in minorVersions: -## scriptSuffixes.append( -## "-python{0}.{1}".format(majorVersion, minorVersion)) + # There could be multiple pyinstaller executables in the path # e.g. for different python variants path = Utilities.getEnvironmentEntry('PATH') @@ -232,30 +235,246 @@ class PyInstallerPlugin(QObject): """ - Class documentation goes here. + Class implementing the PyInstaller interface plug-in. """ def __init__(self, ui): """ Constructor - @param ui reference to the user interface object (UI.UserInterface) + @param ui reference to the user interface object + @type UI.UserInterface """ super(PyInstallerPlugin, self).__init__(ui) self.__ui = ui + + self.__initialize() + _checkProgram() + + self.__translator = None + self.__loadTranslator() + + def __initialize(self): + """ + Private slot to (re)initialize the plug-in. + """ + self.__projectActs = [] def activate(self): """ - Public method to activate this plugin. + Public method to activate this plug-in. @return tuple of None and activation status (boolean) """ global error - error = "" # clear previous error + + # There is already an error, don't activate + if error: + return None, False + + # pyinstaller interface is only activated if it is available + if not _checkProgram(): + return None, False + + # clear previous error + error = "" + + project = e5App().getObject("Project") + menu = project.getMenu("Packagers") + if menu: + # Execute PyInstaller + act = E5Action( + self.tr('Execute PyInstaller'), + self.tr('Execute Py&Installer'), 0, 0, + self, 'packagers_pyinstaller_run') + act.setStatusTip( + self.tr('Generate a distribution package using PyInstaller')) + act.setWhatsThis(self.tr( + """<b>Execute PyInstaller</b>""" + """<p>Generate a distribution package using PyInstaller.""" + """ The command is executed in the project path. All""" + """ files and directories must be given absolute or""" + """ relative to the project directory.</p>""" + )) + act.triggered.connect(self.__pyinstaller) + menu.addAction(act) + self.__projectActs.append(act) + + # Execute pyi-makespec + act = E5Action( + self.tr('Make PyInstaller Spec File'), + self.tr('Make PyInstaller &Spec File'), 0, 0, + self, 'packagers_pyinstaller_spec') + act.setStatusTip( + self.tr('Generate a spec file to be used by PyInstaller')) + act.setWhatsThis(self.tr( + """<b>Make PyInstaller Spec File</b>""" + """<p>Generate a spec file to be used by PyInstaller""" + """ PyInstaller. The command is executed in the project""" + """ path. All files and directories must be given absolute""" + """ or relative to the project directory.</p>""" + )) + act.triggered.connect(self.__pyiMakeSpec) + menu.addAction(act) + self.__projectActs.append(act) + + # clean the pyinstaller created directories + act = E5Action( + self.tr('Clean PyInstaller'), + self.tr('&Clean PyInstaller'), 0, 0, + self, 'packagers_pyinstaller_clean') + act.setStatusTip( + self.tr('Remove the PyInstaller created directories')) + act.setWhatsThis(self.tr( + """<b>Clean PyInstaller</b>""" + """<p>Remove the PyInstaller created directories (dist and""" + """ build). These are subdirectories within the project""" + """ path.""" + )) + act.triggered.connect(self.__pyinstallerCleanup) + menu.addAction(act) + self.__projectActs.append(act) + + project.addE5Actions(self.__projectActs) + project.showMenu.connect(self.__projectShowMenu) return None, True def deactivate(self): """ - Public method to deactivate this plugin. + Public method to deactivate this plug-in. + """ + menu = e5App().getObject("Project").getMenu("Packagers") + if menu: + for act in self.__projectActs: + menu.removeAction(act) + + e5App().getObject("Project").removeE5Actions( + self.__projectActs) + + self.__initialize() + + def __projectShowMenu(self, menuName, menu): + """ + Private slot called, when the the project menu or a submenu is + about to be shown. + + @param menuName name of the menu to be shown + @type str + @param menu reference to the menu + @type QMenu + """ + if menuName == "Packagers": + enable = e5App().getObject("Project").getProjectLanguage() in \ + ["Python", "Python2", "Python3"] + for act in self.__projectActs: + act.setEnabled(enable) + + def __loadTranslator(self): + """ + Private method to load the translation file. + """ + if self.__ui is not None: + loc = self.__ui.getLocale() + if loc and loc != "C": + locale_dir = \ + os.path.join(os.path.dirname(__file__), + "PyInstaller", "i18n") + translation = "pyinstaller_{0}".format(loc) + translator = QTranslator(None) + loaded = translator.load(translation, locale_dir) + if loaded: + self.__translator = translator + e5App().installTranslator(self.__translator) + else: + print("Warning: translation file '{0}' could not be" + " loaded.".format(translation)) + print("Using default.") + + @pyqtSlot() + def __pyinstaller(self): + """ + Private slot to execute the pyinstaller command for the current + project. """ - pass + project = e5App().getObject("Project") + majorVersionStr = project.getProjectLanguage() + if majorVersionStr == "Python3": + executables = [f for f in exePy3 if + f.endswith(("pyinstaller", "pyinstaller.exe"))] + else: + executables = [f for f in exePy2 if + f.endswith(("pyinstaller", "pyinstaller.exe"))] + if not executables: + E5MessageBox.critical( + self.__ui, + self.tr("pyinstaller"), + self.tr("""The pyinstaller executable could not be found.""")) + return + + # check if all files saved and errorfree before continue + if not project.checkAllScriptsDirty(reportSyntaxErrors=True): + return + + from PyInstaller.PyInstallerConfigDialog import PyInstallerConfigDialog + params = project.getData('PACKAGERSPARMS', "PYINSTALLER") + dlg = PyInstallerConfigDialog(project, executables, params, + mode="installer") + if dlg.exec_() == QDialog.Accepted: + args, params = dlg.generateParameters() + project.setData('PACKAGERSPARMS', "PYINSTALLER", params) + + # now do the call + from PyInstaller.PyInstallerExecDialog import PyInstallerExecDialog + dia = PyInstallerExecDialog("pyinstaller") + dia.show() + res = dia.start(args, params, project.getProjectPath(), + project.getMainScript()) + if res: + dia.exec_() + # TODO: implement pyinstaller + + @pyqtSlot() + def __pyiMakeSpec(self): + """ + Private slot to execute the pyi-makespec command for the current + project to generate a spec file to be used by pyinstaller. + """ + project = e5App().getObject("Project") + majorVersionStr = project.getProjectLanguage() + if majorVersionStr == "Python3": + executables = [f for f in exePy3 if + f.endswith(("pyi-makespec", "pyi-makespec.exe"))] + else: + executables = [f for f in exePy2 if + f.endswith(("pyi-makespec", "pyi-makespec.exe"))] + if not executables: + E5MessageBox.critical( + self.__ui, + self.tr("pyi-makespec"), + self.tr("""The pyi-makespec executable could not be found.""")) + return + + # check if all files saved and errorfree before continue + if not project.checkAllScriptsDirty(reportSyntaxErrors=True): + return + + # TODO: implement pyi-makespec + + @pyqtSlot() + def __pyinstallerCleanup(self): + """ + Private slot to remove the directories created by pyinstaller. + """ + project = e5App().getObject("Project") + + from PyInstaller.PyInstallerCleanupDialog import \ + PyInstallerCleanupDialog + dlg = PyInstallerCleanupDialog() + if dlg.exec_() == QDialog.Accepted: + removeDirs = dlg.getDirectories() + for directory in removeDirs: + rd = os.path.join(project.getProjectPath(), directory) + shutil.rmtree(rd, ignore_errors=True) + +# +# eflag: noqa = M801
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyInstaller/PyInstallerCleanupDialog.py Wed Jan 17 16:25:59 2018 +0100 @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2018 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a dialog to select the cleanup action. +""" + +from PyQt5.QtWidgets import QDialog + +from .Ui_PyInstallerCleanupDialog import Ui_PyInstallerCleanupDialog + + +class PyInstallerCleanupDialog(QDialog, Ui_PyInstallerCleanupDialog): + """ + Class implementing a dialog to select the cleanup action. + """ + BuildPath = "build" + DistPath = "dist" + + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent widget + @type QWidget + """ + super(PyInstallerCleanupDialog, self).__init__(parent) + self.setupUi(self) + + msh = self.minimumSizeHint() + self.resize(max(self.width(), msh.width()), msh.height()) + + def getDirectories(self): + """ + Public method to get the project relative directories to be cleaned. + """ + dirs = [] + + if self.buildButton.isChecked() or self.bothButton.isChecked(): + dirs.append(PyInstallerCleanupDialog.BuildPath) + if self.distButton.isChecked() or self.bothButton.isChecked(): + dirs.append(PyInstallerCleanupDialog.DistPath) + + return dirs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyInstaller/PyInstallerCleanupDialog.ui Wed Jan 17 16:25:59 2018 +0100 @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PyInstallerCleanupDialog</class> + <widget class="QDialog" name="PyInstallerCleanupDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>158</height> + </rect> + </property> + <property name="windowTitle"> + <string>Clean PyInstaller</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Select the PyInstaller directories to be removed:</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="buildButton"> + <property name="text"> + <string>Build Directory</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="distButton"> + <property name="text"> + <string>Distribution Directory</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="bothButton"> + <property name="text"> + <string>Both Directories</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>PyInstallerCleanupDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>PyInstallerCleanupDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyInstaller/PyInstallerConfigDialog.py Wed Jan 17 16:25:59 2018 +0100 @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +""" +Module implementing PyInstallerConfigDialog. +""" + +from PyQt5.QtCore import pyqtSlot +from PyQt5.QtWidgets import QDialog + +from E5Gui.E5PathPicker import E5PathPickerModes + +from .Ui_PyInstallerConfigDialog import Ui_PyInstallerConfigDialog + +import Globals + + +class PyInstallerConfigDialog(QDialog, Ui_PyInstallerConfigDialog): + """ + Class documentation goes here. + """ + def __init__(self, project, executables, params=None, mode="installer", + parent=None): + """ + Constructor + + @param project reference to the project object + @type Project.Project + @param executables names of the pyinstaller executables + @type list of str + @param params parameters to set in the dialog + @type dict + @param mode mode of the dialog + @type str (one of 'installer' or 'spec') + @param parent reference to the parent widget + @type QWidget + """ + assert mode in ("installer", "spec") + + super(PyInstallerConfigDialog, self).__init__(parent) + self.setupUi(self) + + self.inputFilePicker.setMode(E5PathPickerModes.OpenFileMode) + self.inputFilePicker.setFilters(self.tr( + "Python Files (*.py *.py2 *.py3);;" + "Python GUI Files (*.pyw *.pyw2 *.pyw3);;" + "Spec Files (*.spec);;" + "All Files (*)" + )) + + self.executableCombo.addItems(executables) + + self.__project = project + if project.getMainScript() == "": + # no main script defined + self.selectedScriptButton.setChecke(True) + self.mainScriptButton.setEnabled(False) + + self.iconFilePicker.setMode(E5PathPickerModes.OpenFileMode) + if Globals.isMacPlatform(): + self.iconFilePicker.setFilters(self.tr( + "Icon Files (*.icns);;" + "All Files (*)" + )) + elif Globals.isWindowsPlatform(): + self.iconFilePicker.setFilters(self.tr( + "Icon Files (*.ico);;" + "Executable Files (*.exe);;" + "All Files (*)" + )) + + self.tabWidget.setTabEnabled( + 1, + Globals.isMacPlatform() or Globals.isWindowsPlatform()) + self.tabWidget.setTabEnabled( + 2, + Globals.isMacPlatform())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyInstaller/PyInstallerConfigDialog.ui Wed Jan 17 16:25:59 2018 +0100 @@ -0,0 +1,386 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PyInstallerConfigDialog</class> + <widget class="QDialog" name="PyInstallerConfigDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>435</height> + </rect> + </property> + <property name="windowTitle"> + <string>PyInstaller Configuration</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="generalTab"> + <attribute name="title"> + <string>&General</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Executable</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QComboBox" name="executableCombo"> + <property name="toolTip"> + <string>Select the executable to be run</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Input File</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QRadioButton" name="mainScriptButton"> + <property name="text"> + <string>Project Main Script</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QRadioButton" name="selectedScriptButton"> + <property name="text"> + <string>Script or Spec File</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="E5PathPicker" name="inputFilePicker" native="true"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="focusPolicy"> + <enum>Qt::StrongFocus</enum> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Generate Option</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QRadioButton" name="oneDirButton"> + <property name="text"> + <string>One Directory</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="oneFileButton"> + <property name="text"> + <string>One File</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Name (optional):</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="E5ClearableLineEdit" name="nameEdit"> + <property name="toolTip"> + <string>Enter an optional name for the application</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Encryption Key (optional):</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="E5ClearableLineEdit" name="keyEdit"> + <property name="toolTip"> + <string>Enter an optional key used to encrypt Python bytecode</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="cleanCheckBox"> + <property name="text"> + <string>Clean Before Building</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="windowsMacTab"> + <attribute name="title"> + <string>&Windows and macOS</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QGroupBox" name="groupBox_4"> + <property name="title"> + <string>Application Mode</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QRadioButton" name="consoleButton"> + <property name="toolTip"> + <string>Select for a console application</string> + </property> + <property name="text"> + <string>Console Application</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="windowedButton"> + <property name="toolTip"> + <string>Select for a windowed application (i.e. do not open a console window)</string> + </property> + <property name="text"> + <string>Windowed Application</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_5"> + <property name="title"> + <string>Icon</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Icon File:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="E5PathPicker" name="iconFilePicker" native="true"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="focusPolicy"> + <enum>Qt::StrongFocus</enum> + </property> + <property name="toolTip"> + <string/> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Icon ID:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="E5ClearableLineEdit" name="iconIdEdit"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string>Enter the icon ID to be extracted from the exe file</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>156</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="macTab"> + <attribute name="title"> + <string>&macOS Specific</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Bundle Identifier:</string> + </property> + </widget> + </item> + <item> + <widget class="E5ClearableLineEdit" name="BundleIdentifierEdit"> + <property name="toolTip"> + <string>Enter the macOS app bundle identifier</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>298</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>E5PathPicker</class> + <extends>QWidget</extends> + <header>E5Gui/E5PathPicker.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>E5ClearableLineEdit</class> + <extends>QLineEdit</extends> + <header>E5Gui/E5LineEdit.h</header> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>tabWidget</tabstop> + <tabstop>executableCombo</tabstop> + <tabstop>mainScriptButton</tabstop> + <tabstop>selectedScriptButton</tabstop> + <tabstop>inputFilePicker</tabstop> + <tabstop>oneDirButton</tabstop> + <tabstop>oneFileButton</tabstop> + <tabstop>nameEdit</tabstop> + <tabstop>keyEdit</tabstop> + <tabstop>cleanCheckBox</tabstop> + <tabstop>consoleButton</tabstop> + <tabstop>windowedButton</tabstop> + <tabstop>iconFilePicker</tabstop> + <tabstop>iconIdEdit</tabstop> + <tabstop>BundleIdentifierEdit</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>PyInstallerConfigDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>227</x> + <y>379</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>PyInstallerConfigDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>295</x> + <y>385</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>selectedScriptButton</sender> + <signal>toggled(bool)</signal> + <receiver>inputFilePicker</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>119</x> + <y>116</y> + </hint> + <hint type="destinationlabel"> + <x>124</x> + <y>150</y> + </hint> + </hints> + </connection> + </connections> +</ui>