예제 #1
0
 def restoreDir(self):
     "用户点击了“默认运行目录”按钮。"
     path = self.txtPath.text().strip()
     fi = QFileInfo(path)
     if path == "" or not fi.exists():
         return
     self.txtDir.setText(fi.dir().absolutePath())
예제 #2
0
파일: simMain.py 프로젝트: yuuzhang/simMain
 def pBtn_RunClicked(self):
     self.pBtn_Run.setEnabled(False)
     global pool,parent_pid
     parent_pid = os.getpid()
   
     fi=QFileInfo(self.lnEdit_Prog.text())
     #runName=fi.baseName()
     ns3path=os.path.dirname(fi.dir().path())
     ns3path=os.path.dirname(ns3path)
     self.exepath=os.path.dirname(ns3path)
     routingName=self.cmBox_RoutingName.currentText()
     simSpan=self.cmBox_SimulationSpan.currentText()
     #global pool
     #jobs=[]
     Thread.daemon=True
     pool = Pool(processes=self.spinBox_processes.value())
     cmds={}
     for traffic in range(string.atoi(self.lnEdit_loadStart.text()),string.atoi(self.lnEdit_loadEnd.text()),
                          string.atoi(self.lnEdit_loadStep.text())):
         if fi.suffix()=="cpp":
             cmds[traffic]="./waf --run='{1} --routingName={3} --simulationSpan={2} --InterestsPerSec={0}'".format(traffic,fi.baseName(),simSpan,routingName)
         else:
             cmds[traffic]="./waf --pyrun='{1} --routingName={3} --simulationSpan={2} --InterestsPerSec={0}'".format(traffic,'src/ndnSIM/examples/'+fi.fileName(),simSpan,routingName)
         res=pool.apply_async(runJob,args=(cmds[traffic],traffic,self.exepath),callback=self.invokeRefresh)
          
         jobs.append(res)
         self.listWidget_activeProg.addItem(cmds[traffic])
         #res.wait()
         #print(res.get())       
     pool.close()    #关闭线程池,不再接收新任务
예제 #3
0
 def restoreDir(self):
     "用户点击了“默认运行目录”按钮。"
     path = self.txtPath.text().strip()
     fi = QFileInfo(path)
     if path == "" or not fi.exists():
         return
     self.txtDir.setText(fi.dir().absolutePath())
예제 #4
0
    def pBtn_RunClicked(self):
        fi = QFileInfo(self.lnEdit_Prog.text())
        #runName=fi.baseName()
        ns3path = os.path.dirname(fi.dir().path())
        ns3path = os.path.dirname(ns3path)
        self.exepath = os.path.dirname(ns3path)
        routingName = self.cmBox_RoutingName.currentText()
        simSpan = self.cmBox_SimulationSpan.currentText()
        pool = Pool(processes=self.spinBox_processes.value())
        cmds = {}
        for traffic in range(string.atoi(self.lnEdit_loadStart.text()),
                             string.atoi(self.lnEdit_loadEnd.text()),
                             string.atoi(self.lnEdit_loadStep.text())):
            if fi.suffix() == "cpp":
                cmds[
                    traffic] = "./waf --run='{1} --routingName={3} --simulationSpan={2} --InterestsPerSec={0}'".format(
                        traffic, fi.baseName(), simSpan, routingName)
            else:
                cmds[
                    traffic] = "./waf --pyrun='{1} --routingName={3} --simulationSpan={2} --InterestsPerSec={0}'".format(
                        traffic, 'src/ndnSIM/examples/' + fi.fileName(),
                        simSpan, routingName)
            res = pool.apply_async(runJob,
                                   args=(cmds[traffic], self.exepath),
                                   callback=self.invokeRefresh)
            self.listWidget_activeProg.addItem(cmds[traffic])
            #res.wait()
            #print(res.get())
        pool.close()  #关闭线程池,不再接收新任务

        #proc=Process(target=ajob.runJob, args=("./waf --run='ndn-zhangyu-multipath --simulationSpan=50 --InterestsPerSec=50'", os.environ['HOME']+"/ndnSIM20170130/ns-3/"))
        #proc.start()
        #proc.join()
        self.refreshGUI("")
예제 #5
0
    def __download(self, item):
        default_path = QDir(item.downloadDirectory()).filePath(
            item.downloadFileName())

        path, _ = QFileDialog.getSaveFileName(self, "Save as", default_path)

        if path is None or len(path) == 0:
            item.cancel()
            return

        info = QFileInfo(path)
        item.setDownloadDirectory(info.dir().path())
        item.setDownloadFileName(info.fileName())
        item.accept()
        self.download_windows.append(
            DownloadProgressWindow(item,
                                   info.dir().path() + "/" + info.fileName()))
예제 #6
0
    def openPrevImage(self):
        # print("openPrevImage")
        fi = QFileInfo(self.filepath)
        if not fi.exists() : return
        filename = fi.fileName()
        basedir = fi.absolutePath()         # This does not include filename
        file_filter = ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.svg", "*.bmp", "*.tiff"]
        image_list = fi.dir().entryList(file_filter)

        index = image_list.index(filename)
        prevfile = image_list[index-1]
        self.openFile(basedir + '/' + prevfile)
예제 #7
0
    def goUP(self):

        path = self.model.filePath(self.rootIndex())

        file_info = QFileInfo(path)
        directory = file_info.dir()
        new_path = directory.absolutePath()

        currentRoot = self.rootIndex()

        self.model.setRootPath(new_path)
        self.setRootIndex(currentRoot.parent())
        self.rootChanged.emit(new_path)
예제 #8
0
    def openNextImage(self):
        fi = QFileInfo(self.filepath)
        if not fi.exists(): return
        filename = fi.fileName()
        basedir = fi.absolutePath()  # This does not include filename
        file_filter = [
            "*.jpg", "*.jpeg", "*.png", "*.gif", "*.svg", "*.bmp", "*.tiff"
        ]
        image_list = fi.dir().entryList(file_filter)

        index = image_list.index(filename)
        if index == len(image_list) - 1: index = -1
        nextfile = image_list[index + 1]
        self.openFile(basedir + '/' + nextfile)
    def __init__(self, editor, filename, text):
        QUndoCommand.__init__(self)
        ChangeFileCommand.fileVersionNumber += 1
        self.editor = editor
        self.filename = filename
        self.setText(text)

        info = QFileInfo(filename)
        dir = info.dir().dirName()
        file = info.fileName()
        self.undoFilename = QDir.tempPath(
        ) + "/FlatSiteBuilder/" + dir + "/" + file + "." + str(
            ChangeFileCommand.fileVersionNumber) + ".undo"
        self.redoFilename = QDir.tempPath(
        ) + "/FlatSiteBuilder/" + dir + "/" + file + "." + str(
            ChangeFileCommand.fileVersionNumber) + ".redo"
예제 #10
0
 def browsePath(self):
     """用户点击了浏览路径的按钮。如果成功设置了路径,就返回True,如果用户取消了操作或者出错,就返回False
     返回的用途参见showEvent()"""
     filename, selectedFilter = QFileDialog.getOpenFileName(self, self.windowTitle())
     if not filename:
         return False
     fi = QFileInfo(filename)
     if fi.isSymLink():
         filename = fi.symLinkTarget()
         if not os.path.exists(filename):
             QMessageBox.information(self, self.windowTitle(), self.tr("快捷方式所指向的程序不正确。"))
             return False
     fi = QFileInfo(filename)
     self.txtName.setText(fi.baseName())
     self.txtPath.setText(fi.absoluteFilePath())
     self.setFileIcon(fi.absoluteFilePath())
     self.txtDir.setText(fi.dir().absolutePath())
     return True
예제 #11
0
 def browsePath(self):
     """用户点击了浏览路径的按钮。如果成功设置了路径,就返回True,如果用户取消了操作或者出错,就返回False
     返回的用途参见showEvent()"""
     filename, selectedFilter = QFileDialog.getOpenFileName(
         self, self.windowTitle())
     if not filename:
         return False
     fi = QFileInfo(filename)
     if fi.isSymLink():
         filename = fi.symLinkTarget()
         if not os.path.exists(filename):
             QMessageBox.information(self, self.windowTitle(),
                                     self.tr("快捷方式所指向的程序不正确。"))
             return False
     fi = QFileInfo(filename)
     self.txtName.setText(fi.baseName())
     self.txtPath.setText(fi.absoluteFilePath())
     self.setFileIcon(fi.absoluteFilePath())
     self.txtDir.setText(fi.dir().absolutePath())
     return True
예제 #12
0
class Project(QObject):
    """ The encapsulation of a project. """

    # The minimum supported project version.  At the moment a project will be
    # automatically updated to the current version when saved.
    min_version = 4

    # The current project version.
    version = 6

    # Emitted when the modification state of the project changes.
    modified_changed = pyqtSignal(bool)

    @property
    def modified(self):
        """ The modified property getter. """

        return self._modified

    @modified.setter
    def modified(self, value):
        """ The modified property setter. """

        if self._modified != value:
            self._modified = value
            self.modified_changed.emit(value)

    # Emitted when the name of the project changes.
    name_changed = pyqtSignal(str)

    @property
    def name(self):
        """ The name property getter. """

        # Use absoluteFilePath() because the file might not exist.
        return self._name.absoluteFilePath() if self._name is not None else ''

    @name.setter
    def name(self, value):
        """ The name property setter. """

        if self._name is None or self._name.absoluteFilePath() != value:
            self._name = QFileInfo(value)
            self.name_changed.emit(value)

    def __init__(self, name=''):
        """ Initialise the project. """

        super().__init__()

        # Initialise the project meta-data.
        self._modified = False
        self._name = QFileInfo(name) if name != '' else None

        # Initialise the project data.
        self.application_name = ''
        self.application_is_pyqt5 = True
        self.application_is_console = False
        self.application_use_py_dll = False
        self.application_is_bundle = True
        self.application_package = QrcPackage()
        self.application_script = ''
        self.application_entry_point = ''
        self.build_dir = 'build'
        self.external_libraries = []
        self.other_extension_modules = []
        self.other_packages = []
        self.pyqt_modules = []
        self.python_host_interpreter = ''
        self.python_use_platform = ['win32']
        self.python_source_dir = '$SYSROOT/src/Python-$PDY_PY_MAJOR.$PDY_PY_MINOR.$PDY_PY_MICRO'
        self.python_ssl = False
        self.python_target_include_dir = '$SYSROOT/include/python$PDY_PY_MAJOR.$PDY_PY_MINOR'
        self.python_target_library = '$SYSROOT/lib/libpython$PDY_PY_MAJOR.$PDY_PY_MINOR.a'
        self.python_target_stdlib_dir = '$SYSROOT/lib/python$PDY_PY_MAJOR.$PDY_PY_MINOR'
        self.python_target_version = get_latest_supported_python_version()
        self.qmake = ''
        self.qmake_configuration = ''
        self.standard_library = []
        self.sys_path = ''

    def path_to_user(self, path):
        """ Convert a file name to one that is relative to the project name if
        possible and uses native separators.
        """

        if self._name is not None:
            rel = self._name.dir().relativeFilePath(path)
            if not rel.startswith('..'):
                path = rel

        return QDir.toNativeSeparators(path)

    def path_from_user(self, user_path):
        """ Convert the name of a file or directory specified by the user to
        the standard Qt format (ie. an absolute path using UNIX separators).  A
        user path may be relative to the name of the project and may contain
        environment variables.
        """

        fi = self._fileinfo_from_user(user_path)

        # Use the canonical name if possible (ie. when the file exists) and
        # fall back to the absolute name.
        path = fi.canonicalFilePath()
        if path == '':
            path = fi.absoluteFilePath()

        return path

    def get_executable_basename(self):
        """ Return the basename of the application executable (i.e. with no
        path or extension.
        """

        if self.application_name != '':
            return self.application_name

        name = self.application_script
        if name == '':
            name = self.application_package.name
            if name == '':
                return ''

        return self._fileinfo_from_user(name).completeBaseName()

    def expandvars(self, path):
        """ Call os.path.expandvars() after expanding some internal values. """

        major, minor, micro = self.python_target_version
        major = str(major)
        minor = str(minor)
        micro = str(micro)

        path = path.replace('$PDY_PY_MAJOR', major)
        path = path.replace('${PDY_PY_MAJOR}', major)

        path = path.replace('$PDY_PY_MINOR', minor)
        path = path.replace('${PDY_PY_MINOR}', minor)

        path = path.replace('$PDY_PY_MICRO', micro)
        path = path.replace('${PDY_PY_MICRO}', micro)

        return os.path.expandvars(path)

    def _fileinfo_from_user(self, user_path):
        """ Convert the name of a file or directory specified by the user to a
        QFileInfo instance.  A user path may be relative to the name of the
        project and may contain environment variables.
        """

        fi = QFileInfo(self.expandvars(user_path.strip()))

        if fi.isRelative() and self._name is not None:
            fi = QFileInfo(self._name.canonicalPath() + '/' + fi.filePath())

        return fi

    def get_stdlib_requirements(self, include_hidden=False):
        """ Return a 2-tuple of the required Python standard library modules
        and the required external libraries.  The modules are a dict with the
        module name as the key and a bool as the value.  The bool is True if
        the module is explicitly required and False if it is implicitly
        required.  The libraries are a set of well known library names.
        """

        # Work out the dependencies.
        metadata = get_python_metadata(self.python_target_version)
        all_modules = {name: _DepState(module)
                for name, module in metadata.items()}

        visit = 0
        for name in all_modules.keys():
            self._set_dependency_state(all_modules, name, visit)
            visit += 1

        # Extract the required modules and libraries.
        required_modules = {}
        required_libraries = set()

        for name, dep_state in all_modules.items():
            if dep_state.explicit:
                explicit = True
            elif dep_state.implicit:
                explicit = False
            else:
                continue

            # Handle any hidden dependencies if required.
            if include_hidden:
                for hidden_dep in dep_state.module.hidden_deps:
                    if hidden_dep not in required_modules:
                        required_modules[hidden_dep] = False

            required_modules[name] = explicit

            if dep_state.module.xlib is not None:
                required_libraries.add(dep_state.module.xlib)

        return required_modules, required_libraries

    def _set_dependency_state(self, all_modules, name, visit, is_dep=False):
        """ Set a module's dependency state. """

        dep_state = all_modules[name]

        if dep_state.visit == visit:
            return

        dep_state.visit = visit

        if dep_state.module.builtin:
            # This will mean that the explicit and implicit states will remain
            # None and so the module will be omitted from the list.
            return

        dep_state.explicit = (name in self.standard_library)

        if dep_state.module.core or is_dep:
            dep_state.implicit = True

        for dep in dep_state.module.deps:
            # If the first character of the module is '?' then it should be
            # excluded if SSL support is disabled.  If the first character is
            # '!' then it should be excluded if SSL support is enabled.
            if dep[0] == '?':
                if not self.python_ssl:
                    continue

                dep = dep[1:]
            elif dep[0] == '!':
                if self.python_ssl:
                    continue

                dep = dep[1:]

            self._set_dependency_state(all_modules, dep, visit,
                    (dep_state.explicit or dep_state.implicit))

    @classmethod
    def load(cls, file_name):
        """ Return a new project loaded from the given file.  Raise a
        UserException if there was an error.
        """

        fi = QFileInfo(file_name)

        tree = ElementTree()

        try:
            root = tree.parse(QDir.toNativeSeparators(fi.canonicalFilePath()))
        except Exception as e:
            raise UserException(
                "There was an error reading the project file.", str(e))

        cls._assert(root.tag == 'Project',
                "Unexpected root tag '{0}', 'Project' expected.".format(
                        root.tag))

        # Check the project version number.
        version = root.get('version')
        cls._assert(version is not None, "Missing 'version' attribute.")

        try:
            version = int(version)
        except:
            version = None

        cls._assert(version is not None, "Invalid 'version'.")

        if version < cls.min_version:
            raise UserException("The project's format is no longer supported.")

        if version > cls.version:
            raise UserException(
                    "The project's format is version {0} but only version {1} is supported.".format(version, cls.version))

        # Create the project and populate it.
        project = cls()
        project._name = fi

        # The Python specific configuration.
        python = root.find('Python')
        cls._assert(python is not None, "Missing 'Python' tag.")

        project.python_host_interpreter = python.get('hostinterpreter', '')

        # This was added in version 5.
        project.python_use_platform = python.get('platformpython', '').split()

        project.python_source_dir = python.get('sourcedir', '')
        project.python_ssl = cls._get_bool(python, 'ssl', 'Python')
        project.python_target_include_dir = python.get('targetincludedir', '')
        project.python_target_library = python.get('targetlibrary', '')
        project.python_target_stdlib_dir = python.get('targetstdlibdir', '')

        major = cls._get_int(python, 'major', 'Python')
        minor = cls._get_int(python, 'minor', 'Python')
        patch = cls._get_int(python, 'patch', 'Python', default=0)
        project.python_target_version = (major, minor, patch)

        # The application specific configuration.
        application = root.find('Application')
        cls._assert(application is not None, "Missing 'Application' tag.")

        project.application_entry_point = application.get('entrypoint', '')
        project.application_is_pyqt5 = cls._get_bool(application, 'ispyqt5',
                'Application')
        project.application_is_console = cls._get_bool(application,
                'isconsole', 'Application')

        project.application_is_bundle = cls._get_bool(application, 'isbundle',
                'Application')
        project.application_name = application.get('name', '')
        project.application_script = application.get('script', '')
        project.sys_path = application.get('syspath', '')

        # Any qmake configuration. This was added in version 5.
        qmake_configuration = application.find('QMakeConfiguration')

        if qmake_configuration is not None:
            project.qmake_configuration = qmake_configuration.text

        # Any application package.
        app_package = application.find('Package')

        if app_package is not None:
            project.application_package = cls._load_package(app_package)
        else:
            project.application_package = QrcPackage()

        # Any PyQt modules.
        for pyqt_m in root.iterfind('PyQtModule'):
            name = pyqt_m.get('name', '')
            cls._assert(name != '',
                    "Missing or empty 'PyQtModule.name' attribute.")
            project.pyqt_modules.append(name)

        # Any standard library modules.
        for stdlib_module_element in root.iterfind('StdlibModule'):
            name = stdlib_module_element.get('name')
            cls._assert(name is not None,
                    "Missing 'StdlibModule.name' attribute.")

            project.standard_library.append(name)

        # Any external C libraries.
        for external_lib_element in root.iterfind('ExternalLib'):
            name = external_lib_element.get('name')
            cls._assert(name is not None,
                    "Missing 'ExternalLib.name' attribute.")

            defines = external_lib_element.get('defines', '')
            includepath = external_lib_element.get('includepath', '')
            libs = external_lib_element.get('libs', '')

            project.external_libraries.append(
                    ExternalLibrary(name, defines, includepath, libs))

        # Any other Python packages.
        project.other_packages = [cls._load_package(package)
                for package in root.iterfind('Package')]

        # Any other extension module.
        for extension_module_element in root.iterfind('ExtensionModule'):
            name = extension_module_element.get('name')
            cls._assert(name is not None,
                    "Missing 'ExtensionModule.name' attribute.")

            qt = extension_module_element.get('qt', '')
            config = extension_module_element.get('config', '')
            sources = extension_module_element.get('sources', '')
            defines = extension_module_element.get('defines', '')
            includepath = extension_module_element.get('includepath', '')
            libs = extension_module_element.get('libs', '')

            project.other_extension_modules.append(
                    ExtensionModule(name, qt, config, sources, defines,
                            includepath, libs))

        # The other configuration.
        others = root.find('Others')
        if others is not None:
            project.build_dir = others.get('builddir', '')
            project.qmake = others.get('qmake', '')

        return project

    def save(self):
        """ Save the project.  Raise a UserException if there was an error. """

        self._save_project(self.name)

    def save_as(self, file_name):
        """ Save the project to the given file and make the file the
        destination of subsequent saves.  Raise a UserException if there was an
        error.
        """

        self._save_project(file_name)

        # Only do this after the project has been successfully saved.
        self.name = file_name

    @classmethod
    def _load_package(cls, package_element):
        """ Return a populated QrcPackage instance. """

        package = QrcPackage()

        package.name = package_element.get('name')
        cls._assert(package.name is not None,
                "Missing 'Package.name' attribute.")

        package.contents = cls._load_mfs_contents(package_element)

        package.exclusions = []
        for exclude_element in package_element.iterfind('Exclude'):
            name = exclude_element.get('name', '')
            cls._assert(name != '',
                    "Missing or empty 'Package.Exclude.name' attribute.")
            package.exclusions.append(name)

        return package

    @classmethod
    def _load_mfs_contents(cls, mfs_element):
        """ Return a list of contents for a memory-filesystem container. """

        contents = []

        for content_element in mfs_element.iterfind('PackageContent'):
            isdir = cls._get_bool(content_element, 'isdirectory',
                    'Package.PackageContent')

            name = content_element.get('name', '')
            cls._assert(name != '',
                    "Missing or empty 'Package.PackageContent.name' attribute.")

            included = cls._get_bool(content_element, 'included',
                    'Package.PackageContent')

            content = QrcDirectory(name, included) if isdir else QrcFile(name, included)

            if isdir:
                content.contents = cls._load_mfs_contents(content_element)

            contents.append(content)

        return contents

    @classmethod
    def _get_bool(cls, element, name, context, default=None):
        """ Get a boolean attribute from an element. """

        value = element.get(name)
        try:
            value = int(value)
        except:
            value = default

        cls._assert(value is not None,
                "Missing or invalid boolean value of '{0}.{1}'.".format(
                        context, name))

        return bool(value)

    @classmethod
    def _get_int(cls, element, name, context, default=None):
        """ Get an integer attribute from an element. """

        value = element.get(name)
        try:
            value = int(value)
        except:
            value = default

        cls._assert(value is not None,
                "Missing or invalid integer value of '{0}.{1}'.".format(
                        context, name))

        return value

    def _save_project(self, file_name):
        """ Save the project to the given file.  Raise a UserException if there
        was an error.
        """

        root = Element('Project', attrib={
            'version': str(self.version)})

        SubElement(root, 'Python', attrib={
            'hostinterpreter': self.python_host_interpreter,
            'platformpython': ' '.join(self.python_use_platform),
            'sourcedir': self.python_source_dir,
            'ssl': str(int(self.python_ssl)),
            'targetincludedir': self.python_target_include_dir,
            'targetlibrary': self.python_target_library,
            'targetstdlibdir': self.python_target_stdlib_dir,
            'major': str(self.python_target_version[0]),
            'minor': str(self.python_target_version[1]),
            'patch': str(self.python_target_version[2])})

        application = SubElement(root, 'Application', attrib={
            'entrypoint': self.application_entry_point,
            'ispyqt5': str(int(self.application_is_pyqt5)),
            'isconsole': str(int(self.application_is_console)),
            'isbundle': str(int(self.application_is_bundle)),
            'name': self.application_name,
            'script': self.application_script,
            'syspath': self.sys_path})

        if self.qmake_configuration != '':
            SubElement(application, 'QMakeConfiguration').text = self.qmake_configuration

        if self.application_package.name is not None:
            self._save_package(application, self.application_package)

        for pyqt_module in self.pyqt_modules:
            SubElement(root, 'PyQtModule', attrib={
                'name': pyqt_module})

        for stdlib_module in self.standard_library:
            SubElement(root, 'StdlibModule', attrib={
                'name': stdlib_module})

        for external_lib in self.external_libraries:
            SubElement(root, 'ExternalLib', attrib={
                'name': external_lib.name,
                'defines': external_lib.defines,
                'includepath': external_lib.includepath,
                'libs': external_lib.libs})

        for package in self.other_packages:
            self._save_package(root, package)

        for extension_module in self.other_extension_modules:
            SubElement(root, 'ExtensionModule', attrib={
                'name': extension_module.name,
                'qt': extension_module.qt,
                'config': extension_module.config,
                'sources': extension_module.sources,
                'defines': extension_module.defines,
                'includepath': extension_module.includepath,
                'libs': extension_module.libs})

        SubElement(root, 'Others', attrib={
            'builddir': self.build_dir,
            'qmake': self.qmake})

        tree = ElementTree(root)

        try:
            tree.write(QDir.toNativeSeparators(file_name), encoding='utf-8',
                    xml_declaration=True)
        except Exception as e:
            raise UserException(
                    "There was an error writing the project file.", str(e))

        self.modified = False

    @classmethod
    def _save_package(cls, container, package):
        """ Save a package in a container element. """

        package_element = SubElement(container, 'Package', attrib={
            'name': package.name})

        cls._save_mfs_contents(package_element, package.contents)

        for exclude in package.exclusions:
            SubElement(package_element, 'Exclude', attrib={
                'name': exclude})

    @classmethod
    def _save_mfs_contents(cls, container, contents):
        """ Save the contents of a memory-filesystem container. """

        for content in contents:
            isdir = isinstance(content, QrcDirectory)

            subcontainer = SubElement(container, 'PackageContent', attrib={
                'name': content.name,
                'included': str(int(content.included)),
                'isdirectory': str(int(isdir))})

            if isdir:
                cls._save_mfs_contents(subcontainer, content.contents)

    @staticmethod
    def _assert(ok, detail):
        """ Validate an assertion and raise a UserException if it failed. """

        if not ok:
            raise UserException("The project file is invalid.", detail)
예제 #13
0
class Project(QObject):
    """ The encapsulation of a project. """

    # The minimum supported project version.  At the moment a project will be
    # automatically updated to the current version when saved.
    min_version = 4

    # The current project version.
    version = 6

    # Emitted when the modification state of the project changes.
    modified_changed = pyqtSignal(bool)

    @property
    def modified(self):
        """ The modified property getter. """

        return self._modified

    @modified.setter
    def modified(self, value):
        """ The modified property setter. """

        if self._modified != value:
            self._modified = value
            self.modified_changed.emit(value)

    # Emitted when the name of the project changes.
    name_changed = pyqtSignal(str)

    @property
    def name(self):
        """ The name property getter. """

        # Use absoluteFilePath() because the file might not exist.
        return self._name.absoluteFilePath() if self._name is not None else ''

    @name.setter
    def name(self, value):
        """ The name property setter. """

        if self._name is None or self._name.absoluteFilePath() != value:
            self._name = QFileInfo(value)
            self.name_changed.emit(value)

    def __init__(self, name=''):
        """ Initialise the project. """

        super().__init__()

        # Initialise the project meta-data.
        self._modified = False
        self._name = QFileInfo(name) if name != '' else None

        # Initialise the project data.
        self.application_name = ''
        self.application_is_pyqt5 = True
        self.application_is_console = False
        self.application_use_py_dll = False
        self.application_is_bundle = True
        self.application_package = QrcPackage()
        self.application_script = ''
        self.application_entry_point = ''
        self.build_dir = 'build'
        self.external_libraries = []
        self.other_extension_modules = []
        self.other_packages = []
        self.pyqt_modules = []
        self.python_host_interpreter = ''
        self.python_use_platform = ['win32']
        self.python_source_dir = '$SYSROOT/src/Python-$PDY_PY_MAJOR.$PDY_PY_MINOR.$PDY_PY_MICRO'
        self.python_ssl = False
        self.python_target_include_dir = '$SYSROOT/include/python$PDY_PY_MAJOR.$PDY_PY_MINOR'
        self.python_target_library = '$SYSROOT/lib/libpython$PDY_PY_MAJOR.$PDY_PY_MINOR.a'
        self.python_target_stdlib_dir = '$SYSROOT/lib/python$PDY_PY_MAJOR.$PDY_PY_MINOR'
        self.python_target_version = get_latest_supported_python_version()
        self.qmake = ''
        self.qmake_configuration = ''
        self.standard_library = []
        self.sys_path = ''

    def path_to_user(self, path):
        """ Convert a file name to one that is relative to the project name if
        possible and uses native separators.
        """

        if self._name is not None:
            rel = self._name.dir().relativeFilePath(path)
            if not rel.startswith('..'):
                path = rel

        return QDir.toNativeSeparators(path)

    def path_from_user(self, user_path):
        """ Convert the name of a file or directory specified by the user to
        the standard Qt format (ie. an absolute path using UNIX separators).  A
        user path may be relative to the name of the project and may contain
        environment variables.
        """

        fi = self._fileinfo_from_user(user_path)

        # Use the canonical name if possible (ie. when the file exists) and
        # fall back to the absolute name.
        path = fi.canonicalFilePath()
        if path == '':
            path = fi.absoluteFilePath()

        return path

    def get_executable_basename(self):
        """ Return the basename of the application executable (i.e. with no
        path or extension.
        """

        if self.application_name != '':
            return self.application_name

        name = self.application_script
        if name == '':
            name = self.application_package.name
            if name == '':
                return ''

        return self._fileinfo_from_user(name).completeBaseName()

    def expandvars(self, path):
        """ Call os.path.expandvars() after expanding some internal values. """

        major, minor, micro = self.python_target_version
        major = str(major)
        minor = str(minor)
        micro = str(micro)

        path = path.replace('$PDY_PY_MAJOR', major)
        path = path.replace('${PDY_PY_MAJOR}', major)

        path = path.replace('$PDY_PY_MINOR', minor)
        path = path.replace('${PDY_PY_MINOR}', minor)

        path = path.replace('$PDY_PY_MICRO', micro)
        path = path.replace('${PDY_PY_MICRO}', micro)

        return os.path.expandvars(path)

    def _fileinfo_from_user(self, user_path):
        """ Convert the name of a file or directory specified by the user to a
        QFileInfo instance.  A user path may be relative to the name of the
        project and may contain environment variables.
        """

        fi = QFileInfo(self.expandvars(user_path.strip()))

        if fi.isRelative() and self._name is not None:
            fi = QFileInfo(self._name.canonicalPath() + '/' + fi.filePath())

        return fi

    def get_stdlib_requirements(self, include_hidden=False):
        """ Return a 2-tuple of the required Python standard library modules
        and the required external libraries.  The modules are a dict with the
        module name as the key and a bool as the value.  The bool is True if
        the module is explicitly required and False if it is implicitly
        required.  The libraries are a set of well known library names.
        """

        # Work out the dependencies.
        metadata = get_python_metadata(self.python_target_version)
        all_modules = {
            name: _DepState(module)
            for name, module in metadata.items()
        }

        visit = 0
        for name in all_modules.keys():
            self._set_dependency_state(all_modules, name, visit)
            visit += 1

        # Extract the required modules and libraries.
        required_modules = {}
        required_libraries = set()

        for name, dep_state in all_modules.items():
            if dep_state.explicit:
                explicit = True
            elif dep_state.implicit:
                explicit = False
            else:
                continue

            # Handle any hidden dependencies if required.
            if include_hidden:
                for hidden_dep in dep_state.module.hidden_deps:
                    if hidden_dep not in required_modules:
                        required_modules[hidden_dep] = False

            required_modules[name] = explicit

            if dep_state.module.xlib is not None:
                required_libraries.add(dep_state.module.xlib)

        return required_modules, required_libraries

    def _set_dependency_state(self, all_modules, name, visit, is_dep=False):
        """ Set a module's dependency state. """

        dep_state = all_modules[name]

        if dep_state.visit == visit:
            return

        dep_state.visit = visit

        if dep_state.module.builtin:
            # This will mean that the explicit and implicit states will remain
            # None and so the module will be omitted from the list.
            return

        dep_state.explicit = (name in self.standard_library)

        if dep_state.module.core or is_dep:
            dep_state.implicit = True

        for dep in dep_state.module.deps:
            # If the first character of the module is '?' then it should be
            # excluded if SSL support is disabled.  If the first character is
            # '!' then it should be excluded if SSL support is enabled.
            if dep[0] == '?':
                if not self.python_ssl:
                    continue

                dep = dep[1:]
            elif dep[0] == '!':
                if self.python_ssl:
                    continue

                dep = dep[1:]

            self._set_dependency_state(
                all_modules, dep, visit,
                (dep_state.explicit or dep_state.implicit))

    @classmethod
    def load(cls, file_name):
        """ Return a new project loaded from the given file.  Raise a
        UserException if there was an error.
        """

        fi = QFileInfo(file_name)

        tree = ElementTree()

        try:
            root = tree.parse(QDir.toNativeSeparators(fi.canonicalFilePath()))
        except Exception as e:
            raise UserException("There was an error reading the project file.",
                                str(e))

        cls._assert(
            root.tag == 'Project',
            "Unexpected root tag '{0}', 'Project' expected.".format(root.tag))

        # Check the project version number.
        version = root.get('version')
        cls._assert(version is not None, "Missing 'version' attribute.")

        try:
            version = int(version)
        except:
            version = None

        cls._assert(version is not None, "Invalid 'version'.")

        if version < cls.min_version:
            raise UserException("The project's format is no longer supported.")

        if version > cls.version:
            raise UserException(
                "The project's format is version {0} but only version {1} is supported."
                .format(version, cls.version))

        # Create the project and populate it.
        project = cls()
        project._name = fi

        # The Python specific configuration.
        python = root.find('Python')
        cls._assert(python is not None, "Missing 'Python' tag.")

        project.python_host_interpreter = python.get('hostinterpreter', '')

        # This was added in version 5.
        project.python_use_platform = python.get('platformpython', '').split()

        project.python_source_dir = python.get('sourcedir', '')
        project.python_ssl = cls._get_bool(python, 'ssl', 'Python')
        project.python_target_include_dir = python.get('targetincludedir', '')
        project.python_target_library = python.get('targetlibrary', '')
        project.python_target_stdlib_dir = python.get('targetstdlibdir', '')

        major = cls._get_int(python, 'major', 'Python')
        minor = cls._get_int(python, 'minor', 'Python')
        patch = cls._get_int(python, 'patch', 'Python', default=0)
        project.python_target_version = (major, minor, patch)

        # The application specific configuration.
        application = root.find('Application')
        cls._assert(application is not None, "Missing 'Application' tag.")

        project.application_entry_point = application.get('entrypoint', '')
        project.application_is_pyqt5 = cls._get_bool(application, 'ispyqt5',
                                                     'Application')
        project.application_is_console = cls._get_bool(application,
                                                       'isconsole',
                                                       'Application')

        project.application_is_bundle = cls._get_bool(application, 'isbundle',
                                                      'Application')
        project.application_name = application.get('name', '')
        project.application_script = application.get('script', '')
        project.sys_path = application.get('syspath', '')

        # Any qmake configuration. This was added in version 5.
        qmake_configuration = application.find('QMakeConfiguration')

        if qmake_configuration is not None:
            project.qmake_configuration = qmake_configuration.text

        # Any application package.
        app_package = application.find('Package')

        if app_package is not None:
            project.application_package = cls._load_package(app_package)
        else:
            project.application_package = QrcPackage()

        # Any PyQt modules.
        for pyqt_m in root.iterfind('PyQtModule'):
            name = pyqt_m.get('name', '')
            cls._assert(name != '',
                        "Missing or empty 'PyQtModule.name' attribute.")
            project.pyqt_modules.append(name)

        # Any standard library modules.
        for stdlib_module_element in root.iterfind('StdlibModule'):
            name = stdlib_module_element.get('name')
            cls._assert(name is not None,
                        "Missing 'StdlibModule.name' attribute.")

            project.standard_library.append(name)

        # Any external C libraries.
        for external_lib_element in root.iterfind('ExternalLib'):
            name = external_lib_element.get('name')
            cls._assert(name is not None,
                        "Missing 'ExternalLib.name' attribute.")

            defines = external_lib_element.get('defines', '')
            includepath = external_lib_element.get('includepath', '')
            libs = external_lib_element.get('libs', '')

            project.external_libraries.append(
                ExternalLibrary(name, defines, includepath, libs))

        # Any other Python packages.
        project.other_packages = [
            cls._load_package(package) for package in root.iterfind('Package')
        ]

        # Any other extension module.
        for extension_module_element in root.iterfind('ExtensionModule'):
            name = extension_module_element.get('name')
            cls._assert(name is not None,
                        "Missing 'ExtensionModule.name' attribute.")

            qt = extension_module_element.get('qt', '')
            config = extension_module_element.get('config', '')
            sources = extension_module_element.get('sources', '')
            defines = extension_module_element.get('defines', '')
            includepath = extension_module_element.get('includepath', '')
            libs = extension_module_element.get('libs', '')

            project.other_extension_modules.append(
                ExtensionModule(name, qt, config, sources, defines,
                                includepath, libs))

        # The other configuration.
        others = root.find('Others')
        if others is not None:
            project.build_dir = others.get('builddir', '')
            project.qmake = others.get('qmake', '')

        return project

    def save(self):
        """ Save the project.  Raise a UserException if there was an error. """

        self._save_project(self.name)

    def save_as(self, file_name):
        """ Save the project to the given file and make the file the
        destination of subsequent saves.  Raise a UserException if there was an
        error.
        """

        self._save_project(file_name)

        # Only do this after the project has been successfully saved.
        self.name = file_name

    @classmethod
    def _load_package(cls, package_element):
        """ Return a populated QrcPackage instance. """

        package = QrcPackage()

        package.name = package_element.get('name')
        cls._assert(package.name is not None,
                    "Missing 'Package.name' attribute.")

        package.contents = cls._load_mfs_contents(package_element)

        package.exclusions = []
        for exclude_element in package_element.iterfind('Exclude'):
            name = exclude_element.get('name', '')
            cls._assert(name != '',
                        "Missing or empty 'Package.Exclude.name' attribute.")
            package.exclusions.append(name)

        return package

    @classmethod
    def _load_mfs_contents(cls, mfs_element):
        """ Return a list of contents for a memory-filesystem container. """

        contents = []

        for content_element in mfs_element.iterfind('PackageContent'):
            isdir = cls._get_bool(content_element, 'isdirectory',
                                  'Package.PackageContent')

            name = content_element.get('name', '')
            cls._assert(
                name != '',
                "Missing or empty 'Package.PackageContent.name' attribute.")

            included = cls._get_bool(content_element, 'included',
                                     'Package.PackageContent')

            content = QrcDirectory(name, included) if isdir else QrcFile(
                name, included)

            if isdir:
                content.contents = cls._load_mfs_contents(content_element)

            contents.append(content)

        return contents

    @classmethod
    def _get_bool(cls, element, name, context, default=None):
        """ Get a boolean attribute from an element. """

        value = element.get(name)
        try:
            value = int(value)
        except:
            value = default

        cls._assert(
            value is not None,
            "Missing or invalid boolean value of '{0}.{1}'.".format(
                context, name))

        return bool(value)

    @classmethod
    def _get_int(cls, element, name, context, default=None):
        """ Get an integer attribute from an element. """

        value = element.get(name)
        try:
            value = int(value)
        except:
            value = default

        cls._assert(
            value is not None,
            "Missing or invalid integer value of '{0}.{1}'.".format(
                context, name))

        return value

    def _save_project(self, file_name):
        """ Save the project to the given file.  Raise a UserException if there
        was an error.
        """

        root = Element('Project', attrib={'version': str(self.version)})

        SubElement(root,
                   'Python',
                   attrib={
                       'hostinterpreter': self.python_host_interpreter,
                       'platformpython': ' '.join(self.python_use_platform),
                       'sourcedir': self.python_source_dir,
                       'ssl': str(int(self.python_ssl)),
                       'targetincludedir': self.python_target_include_dir,
                       'targetlibrary': self.python_target_library,
                       'targetstdlibdir': self.python_target_stdlib_dir,
                       'major': str(self.python_target_version[0]),
                       'minor': str(self.python_target_version[1]),
                       'patch': str(self.python_target_version[2])
                   })

        application = SubElement(root,
                                 'Application',
                                 attrib={
                                     'entrypoint':
                                     self.application_entry_point,
                                     'ispyqt5':
                                     str(int(self.application_is_pyqt5)),
                                     'isconsole':
                                     str(int(self.application_is_console)),
                                     'isbundle':
                                     str(int(self.application_is_bundle)),
                                     'name':
                                     self.application_name,
                                     'script':
                                     self.application_script,
                                     'syspath':
                                     self.sys_path
                                 })

        if self.qmake_configuration != '':
            SubElement(application,
                       'QMakeConfiguration').text = self.qmake_configuration

        if self.application_package.name is not None:
            self._save_package(application, self.application_package)

        for pyqt_module in self.pyqt_modules:
            SubElement(root, 'PyQtModule', attrib={'name': pyqt_module})

        for stdlib_module in self.standard_library:
            SubElement(root, 'StdlibModule', attrib={'name': stdlib_module})

        for external_lib in self.external_libraries:
            SubElement(root,
                       'ExternalLib',
                       attrib={
                           'name': external_lib.name,
                           'defines': external_lib.defines,
                           'includepath': external_lib.includepath,
                           'libs': external_lib.libs
                       })

        for package in self.other_packages:
            self._save_package(root, package)

        for extension_module in self.other_extension_modules:
            SubElement(root,
                       'ExtensionModule',
                       attrib={
                           'name': extension_module.name,
                           'qt': extension_module.qt,
                           'config': extension_module.config,
                           'sources': extension_module.sources,
                           'defines': extension_module.defines,
                           'includepath': extension_module.includepath,
                           'libs': extension_module.libs
                       })

        SubElement(root,
                   'Others',
                   attrib={
                       'builddir': self.build_dir,
                       'qmake': self.qmake
                   })

        tree = ElementTree(root)

        try:
            tree.write(QDir.toNativeSeparators(file_name),
                       encoding='utf-8',
                       xml_declaration=True)
        except Exception as e:
            raise UserException("There was an error writing the project file.",
                                str(e))

        self.modified = False

    @classmethod
    def _save_package(cls, container, package):
        """ Save a package in a container element. """

        package_element = SubElement(container,
                                     'Package',
                                     attrib={'name': package.name})

        cls._save_mfs_contents(package_element, package.contents)

        for exclude in package.exclusions:
            SubElement(package_element, 'Exclude', attrib={'name': exclude})

    @classmethod
    def _save_mfs_contents(cls, container, contents):
        """ Save the contents of a memory-filesystem container. """

        for content in contents:
            isdir = isinstance(content, QrcDirectory)

            subcontainer = SubElement(container,
                                      'PackageContent',
                                      attrib={
                                          'name': content.name,
                                          'included':
                                          str(int(content.included)),
                                          'isdirectory': str(int(isdir))
                                      })

            if isdir:
                cls._save_mfs_contents(subcontainer, content.contents)

    @staticmethod
    def _assert(ok, detail):
        """ Validate an assertion and raise a UserException if it failed. """

        if not ok:
            raise UserException("The project file is invalid.", detail)