def description_from_id(context, space_type, idname, *, use_operator=True):
    # Used directly for tooltips.
    _cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(
        context, space_type, idname)
    if item is None:
        return False

    # Custom description.
    description = item.description
    if description is not None:
        if callable(description):
            km = _keymap_from_item(context, item)
            return description(context, item, km)
        return tip_(description)

    # Extract from the operator.
    if use_operator:
        operator = item.operator
        if operator is None:
            if item.keymap is not None:
                km = _keymap_from_item(context, item)
                if km is not None:
                    for kmi in km.keymap_items:
                        if kmi.active:
                            operator = kmi.idname
                            break

        if operator is not None:
            import _bpy
            return tip_(_bpy.ops.get_rna_type(operator).description)
    return ""
Пример #2
0
    def _draw_framerate_label(*args):
        # avoids re-creating text string each draw
        if RENDER_PT_dimensions._frame_rate_args_prev == args:
            return RENDER_PT_dimensions._frame_rate_ret

        fps, fps_base, preset_label = args

        if fps_base == 1.0:
            fps_rate = round(fps)
        else:
            fps_rate = round(fps / fps_base, 2)

        # TODO: Change the following to iterate over existing presets
        custom_framerate = (fps_rate
                            not in {23.98, 24, 25, 29.97, 30, 50, 59.94, 60})

        if custom_framerate is True:
            fps_label_text = tip_("Custom (%.4g fps)") % fps_rate
            show_framerate = True
        else:
            fps_label_text = tip_("%.4g fps") % fps_rate
            show_framerate = (preset_label == "Custom")

        RENDER_PT_dimensions._frame_rate_args_prev = args
        RENDER_PT_dimensions._frame_rate_ret = args = (fps_label_text,
                                                       show_framerate)
        return args
Пример #3
0
    def execute(self, context):
        import os
        prefs = context.preferences
        wm = context.window_manager
        filename = bpy.path.ensure_ext(self.filename, ".sl")

        path_studiolights = bpy.utils.user_resource('DATAFILES', os.path.join("studiolights", "studio"), create=True)
        if not path_studiolights:
            self.report({'ERROR'}, "Failed to get Studio Light path")
            return {'CANCELLED'}

        filepath_final = os.path.join(path_studiolights, filename)
        if os.path.isfile(filepath_final):
            if not self.ask_overide:
                self.ask_overide = True
                return wm.invoke_props_dialog(self, width=600)
            else:
                for studio_light in prefs.studio_lights:
                    if studio_light.name == filename:
                        bpy.ops.preferences.studiolight_uninstall(index=studio_light.index)

        prefs.studio_lights.new(path=filepath_final)

        # print message
        msg = (
            tip_("StudioLight Installed %r into %r") %
            (self.filename, str(path_studiolights))
        )
        print(msg)
        self.report({'INFO'}, msg)
        return {'FINISHED'}
Пример #4
0
    def execute(self, context):
        import os
        import shutil
        prefs = context.preferences

        path_studiolights = os.path.join("studiolights", self.type.lower())
        path_studiolights = bpy.utils.user_resource('DATAFILES',
                                                    path_studiolights,
                                                    create=True)
        if not path_studiolights:
            self.report({'ERROR'}, "Failed to create Studio Light path")
            return {'CANCELLED'}

        for e in self.files:
            shutil.copy(os.path.join(self.directory, e.name),
                        path_studiolights)
            prefs.studio_lights.load(os.path.join(path_studiolights, e.name),
                                     self.type)

        # print message
        msg = (tip_("StudioLight Installed %r into %r") %
               (", ".join(e.name for e in self.files), path_studiolights))
        print(msg)
        self.report({'INFO'}, msg)
        return {'FINISHED'}
Пример #5
0
 def description(cls, _context, properties):
     nodetype = properties["type"]
     bl_rna = bpy.types.Node.bl_rna_get_subclass(nodetype)
     if bl_rna is not None:
         return tip_(bl_rna.description)
     else:
         return ""
Пример #6
0
    def execute(self, context):
        import os
        prefs = context.preferences
        wm = context.window_manager
        filename = bpy.path.ensure_ext(self.filename, ".sl")

        path_studiolights = bpy.utils.user_resource('DATAFILES', os.path.join("studiolights", "studio"), create=True)
        if not path_studiolights:
            self.report({'ERROR'}, "Failed to get Studio Light path")
            return {'CANCELLED'}

        filepath_final = os.path.join(path_studiolights, filename)
        if os.path.isfile(filepath_final):
            if not self.ask_overide:
                self.ask_overide = True
                return wm.invoke_props_dialog(self, width=600)
            else:
                for studio_light in prefs.studio_lights:
                    if studio_light.name == filename:
                        bpy.ops.preferences.studiolight_uninstall(index=studio_light.index)

        prefs.studio_lights.new(path=filepath_final)

        # print message
        msg = (
            tip_("StudioLight Installed %r into %r") %
            (self.filename, str(path_studiolights))
        )
        print(msg)
        self.report({'INFO'}, msg)
        return {'FINISHED'}
Пример #7
0
    def execute(self, context):
        import os
        import shutil
        prefs = context.preferences

        path_studiolights = os.path.join("studiolights", self.type.lower())
        path_studiolights = bpy.utils.user_resource('DATAFILES', path_studiolights, create=True)
        if not path_studiolights:
            self.report({'ERROR'}, "Failed to create Studio Light path")
            return {'CANCELLED'}

        for e in self.files:
            shutil.copy(os.path.join(self.directory, e.name), path_studiolights)
            prefs.studio_lights.load(os.path.join(path_studiolights, e.name), self.type)

        # print message
        msg = (
            tip_("StudioLight Installed %r into %r") %
            (", ".join(e.name for e in self.files), path_studiolights)
        )
        print(msg)
        self.report({'INFO'}, msg)
        return {'FINISHED'}
Пример #8
0
    def execute(self, context):
        import os

        preferences = context.preferences
        scene = context.scene
        view_layer = context.view_layer

        in_filepath = self.input_filepath
        out_filepath = self.output_filepath

        in_filepaths = []
        out_filepaths = []

        if in_filepath != '':
            # Denoise a single file
            if out_filepath == '':
                out_filepath = in_filepath

            in_filepaths.append(in_filepath)
            out_filepaths.append(out_filepath)
        else:
            # Denoise animation sequence with expanded frames matching
            # Blender render output file naming.
            in_filepath = scene.render.filepath
            if out_filepath == '':
                out_filepath = in_filepath

            # Backup since we will overwrite the scene path temporarily
            original_filepath = scene.render.filepath

            for frame in range(scene.frame_start, scene.frame_end + 1):
                scene.render.filepath = in_filepath
                filepath = scene.render.frame_path(frame=frame)
                in_filepaths.append(filepath)

                if not os.path.isfile(filepath):
                    scene.render.filepath = original_filepath
                    err_msg = tip_("Frame '%s' not found, animation must be complete") % filepath
                    self.report({'ERROR'}, err_msg)
                    return {'CANCELLED'}

                scene.render.filepath = out_filepath
                filepath = scene.render.frame_path(frame=frame)
                out_filepaths.append(filepath)

            scene.render.filepath = original_filepath

        # Run denoiser
        # TODO: support cancel and progress reports.
        import _cycles
        try:
            _cycles.denoise(preferences.as_pointer(),
                            scene.as_pointer(),
                            view_layer.as_pointer(),
                            input=in_filepaths,
                            output=out_filepaths)
        except Exception as e:
            self.report({'ERROR'}, str(e))
            return {'FINISHED'}

        self.report({'INFO'}, "Denoising completed")
        return {'FINISHED'}
Пример #9
0
    def execute(self, _context):
        import traceback
        import zipfile
        import os

        filepath = self.filepath

        path_app_templates = bpy.utils.user_resource(
            'SCRIPTS', os.path.join("startup", "bl_app_templates_user"),
            create=True,
        )

        if not path_app_templates:
            self.report({'ERROR'}, "Failed to get add-ons path")
            return {'CANCELLED'}

        if not os.path.isdir(path_app_templates):
            try:
                os.makedirs(path_app_templates, exist_ok=True)
            except:
                traceback.print_exc()

        app_templates_old = set(os.listdir(path_app_templates))

        # check to see if the file is in compressed format (.zip)
        if zipfile.is_zipfile(filepath):
            try:
                file_to_extract = zipfile.ZipFile(filepath, 'r')
            except:
                traceback.print_exc()
                return {'CANCELLED'}

            if self.overwrite:
                for f in file_to_extract.namelist():
                    module_filesystem_remove(path_app_templates, f)
            else:
                for f in file_to_extract.namelist():
                    path_dest = os.path.join(path_app_templates, os.path.basename(f))
                    if os.path.exists(path_dest):
                        self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
                        return {'CANCELLED'}

            try:  # extract the file to "bl_app_templates_user"
                file_to_extract.extractall(path_app_templates)
            except:
                traceback.print_exc()
                return {'CANCELLED'}

        else:
            # Only support installing zipfiles
            self.report({'WARNING'}, "Expected a zip-file %r\n" % filepath)
            return {'CANCELLED'}

        app_templates_new = set(os.listdir(path_app_templates)) - app_templates_old

        # in case a new module path was created to install this addon.
        bpy.utils.refresh_script_paths()

        # print message
        msg = (
            tip_("Template Installed (%s) from %r into %r") %
            (", ".join(sorted(app_templates_new)), filepath, path_app_templates)
        )
        print(msg)
        self.report({'INFO'}, msg)

        return {'FINISHED'}
Пример #10
0
    def execute(self, context):
        import addon_utils
        import traceback
        import zipfile
        import shutil
        import os

        pyfile = self.filepath

        if self.target == 'DEFAULT':
            # don't use bpy.utils.script_paths("addons") because we may not be able to write to it.
            path_addons = bpy.utils.user_resource('SCRIPTS', "addons", create=True)
        else:
            path_addons = context.preferences.filepaths.script_directory
            if path_addons:
                path_addons = os.path.join(path_addons, "addons")

        if not path_addons:
            self.report({'ERROR'}, "Failed to get add-ons path")
            return {'CANCELLED'}

        if not os.path.isdir(path_addons):
            try:
                os.makedirs(path_addons, exist_ok=True)
            except:
                traceback.print_exc()

        # Check if we are installing from a target path,
        # doing so causes 2+ addons of same name or when the same from/to
        # location is used, removal of the file!
        addon_path = ""
        pyfile_dir = os.path.dirname(pyfile)
        for addon_path in addon_utils.paths():
            if os.path.samefile(pyfile_dir, addon_path):
                self.report({'ERROR'}, "Source file is in the add-on search path: %r" % addon_path)
                return {'CANCELLED'}
        del addon_path
        del pyfile_dir
        # done checking for exceptional case

        addons_old = {mod.__name__ for mod in addon_utils.modules()}

        # check to see if the file is in compressed format (.zip)
        if zipfile.is_zipfile(pyfile):
            try:
                file_to_extract = zipfile.ZipFile(pyfile, 'r')
            except:
                traceback.print_exc()
                return {'CANCELLED'}

            if self.overwrite:
                for f in file_to_extract.namelist():
                    module_filesystem_remove(path_addons, f)
            else:
                for f in file_to_extract.namelist():
                    path_dest = os.path.join(path_addons, os.path.basename(f))
                    if os.path.exists(path_dest):
                        self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
                        return {'CANCELLED'}

            try:  # extract the file to "addons"
                file_to_extract.extractall(path_addons)
            except:
                traceback.print_exc()
                return {'CANCELLED'}

        else:
            path_dest = os.path.join(path_addons, os.path.basename(pyfile))

            if self.overwrite:
                module_filesystem_remove(path_addons, os.path.basename(pyfile))
            elif os.path.exists(path_dest):
                self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
                return {'CANCELLED'}

            # if not compressed file just copy into the addon path
            try:
                shutil.copyfile(pyfile, path_dest)
            except:
                traceback.print_exc()
                return {'CANCELLED'}

        addons_new = {mod.__name__ for mod in addon_utils.modules()} - addons_old
        addons_new.discard("modules")

        # disable any addons we may have enabled previously and removed.
        # this is unlikely but do just in case. bug [#23978]
        for new_addon in addons_new:
            addon_utils.disable(new_addon, default_set=True)

        # possible the zip contains multiple addons, we could disallow this
        # but for now just use the first
        for mod in addon_utils.modules(refresh=False):
            if mod.__name__ in addons_new:
                info = addon_utils.module_bl_info(mod)

                # show the newly installed addon.
                context.window_manager.addon_filter = 'All'
                context.window_manager.addon_search = info["name"]
                break

        # in case a new module path was created to install this addon.
        bpy.utils.refresh_script_paths()

        # print message
        msg = (
            tip_("Modules Installed (%s) from %r into %r") %
            (", ".join(sorted(addons_new)), pyfile, path_addons)
        )
        print(msg)
        self.report({'INFO'}, msg)

        return {'FINISHED'}
    def execute(self, context):
        import os
        import subprocess
        from shlex import quote

        scene = context.scene
        rd = scene.render
        prefs = context.preferences
        fps_final = rd.fps / rd.fps_base

        preset = prefs.filepaths.animation_player_preset
        # file_path = bpy.path.abspath(rd.filepath)  # UNUSED
        is_movie = rd.is_movie_format

        views_format = rd.image_settings.views_format
        if rd.use_multiview and views_format == 'INDIVIDUAL':
            view_suffix = rd.views.active.file_suffix
        else:
            view_suffix = ""

        # try and guess a command line if it doesn't exist
        if preset == 'CUSTOM':
            player_path = prefs.filepaths.animation_player
        else:
            player_path = guess_player_path(preset)

        if is_movie is False and preset in {'FRAMECYCLER', 'RV', 'MPLAYER'}:
            # replace the number with '#'
            file_a = rd.frame_path(frame=0, view=view_suffix)

            # TODO, make an api call for this
            frame_tmp = 9
            file_b = rd.frame_path(frame=frame_tmp, view=view_suffix)

            while len(file_a) == len(file_b):
                frame_tmp = (frame_tmp * 10) + 9
                file_b = rd.frame_path(frame=frame_tmp, view=view_suffix)
            file_b = rd.frame_path(frame=int(frame_tmp / 10), view=view_suffix)

            file = ("".join(
                (c if file_b[i] == c else "#") for i, c in enumerate(file_a)))
            del file_a, file_b, frame_tmp
            file = bpy.path.abspath(file)  # expand '//'
        else:
            path_valid = True
            # works for movies and images
            file = rd.frame_path(frame=scene.frame_start,
                                 preview=scene.use_preview_range,
                                 view=view_suffix)
            file = bpy.path.abspath(file)  # expand '//'
            if not os.path.exists(file):
                err_msg = tip_("File %r not found") % file
                self.report({'WARNING'}, err_msg)
                path_valid = False

            # one last try for full range if we used preview range
            if scene.use_preview_range and not path_valid:
                file = rd.frame_path(frame=scene.frame_start,
                                     preview=False,
                                     view=view_suffix)
                file = bpy.path.abspath(file)  # expand '//'
                err_msg = tip_("File %r not found") % file
                if not os.path.exists(file):
                    self.report({'WARNING'}, err_msg)

        cmd = [player_path]
        # extra options, fps controls etc.
        if scene.use_preview_range:
            frame_start = scene.frame_preview_start
            frame_end = scene.frame_preview_end
        else:
            frame_start = scene.frame_start
            frame_end = scene.frame_end
        if preset == 'INTERNAL':
            opts = [
                "-a",
                "-f",
                str(rd.fps),
                str(rd.fps_base),
                "-s",
                str(frame_start),
                "-e",
                str(frame_end),
                "-j",
                str(scene.frame_step),
                "-c",
                str(prefs.system.memory_cache_limit),
                file,
            ]
            cmd.extend(opts)
        elif preset == 'DJV':
            opts = [
                file, "-speed",
                str(fps_final), "-in_out",
                str(frame_start),
                str(frame_end), "-frame",
                str(scene.frame_current), "-time_units", "Frames"
            ]
            cmd.extend(opts)
        elif preset == 'FRAMECYCLER':
            opts = [file, "%d-%d" % (scene.frame_start, scene.frame_end)]
            cmd.extend(opts)
        elif preset == 'RV':
            opts = ["-fps", str(rd.fps), "-play", "[ %s ]" % file]
            cmd.extend(opts)
        elif preset == 'MPLAYER':
            opts = []
            if is_movie:
                opts.append(file)
            else:
                opts += [
                    ("mf://" + file.replace("#", "?")),
                    "-mf",
                    "fps=%.4f" % fps_final,
                ]

            opts += ["-loop", "0", "-really-quiet", "-fs"]
            cmd.extend(opts)
        else:  # 'CUSTOM'
            cmd.append(file)

        # launch it
        print("Executing command:\n ", " ".join(quote(c) for c in cmd))

        try:
            subprocess.Popen(cmd)
        except Exception as e:
            err_msg = tip_(
                "Couldn't run external animation player with command %r\n%s"
            ) % (cmd, e)
            self.report(
                {'ERROR'},
                err_msg,
            )
            return {'CANCELLED'}

        return {'FINISHED'}
    def execute(self, context):
        import subprocess
        from shlex import quote

        scene = context.scene
        rd = scene.render
        prefs = context.preferences
        fps_final = rd.fps / rd.fps_base

        preset = prefs.filepaths.animation_player_preset
        player_path = prefs.filepaths.animation_player
        # file_path = bpy.path.abspath(rd.filepath)  # UNUSED
        is_movie = rd.is_movie_format

        # try and guess a command line if it doesn't exist
        if player_path == "":
            player_path = guess_player_path(preset)

        if is_movie is False and preset in {'FRAMECYCLER', 'RV', 'MPLAYER'}:
            # replace the number with '#'
            file_a = rd.frame_path(frame=0)

            # TODO, make an api call for this
            frame_tmp = 9
            file_b = rd.frame_path(frame=frame_tmp)

            while len(file_a) == len(file_b):
                frame_tmp = (frame_tmp * 10) + 9
                file_b = rd.frame_path(frame=frame_tmp)
            file_b = rd.frame_path(frame=int(frame_tmp / 10))

            file = ("".join(
                (c if file_b[i] == c else "#") for i, c in enumerate(file_a)))
            del file_a, file_b, frame_tmp
            file = bpy.path.abspath(file)  # expand '//'
        else:
            path_valid = True
            # works for movies and images
            file = rd.frame_path(frame=scene.frame_start,
                                 preview=scene.use_preview_range)
            file = bpy.path.abspath(file)  # expand '//'
            if not os.path.exists(file):
                err_msg = tip_("File %r not found") % file
                self.report({'WARNING'}, err_msg)
                path_valid = False

            # one last try for full range if we used preview range
            if scene.use_preview_range and not path_valid:
                file = rd.frame_path(frame=scene.frame_start, preview=False)
                file = bpy.path.abspath(file)  # expand '//'
                err_msg = tip_("File %r not found") % file
                if not os.path.exists(file):
                    self.report({'WARNING'}, err_msg)

        cmd = [player_path]
        # extra options, fps controls etc.
        if scene.use_preview_range:
            frame_start = scene.frame_preview_start
            frame_end = scene.frame_preview_end
        else:
            frame_start = scene.frame_start
            frame_end = scene.frame_end
        if preset == 'INTERNAL':
            opts = [
                "-a",
                "-f",
                str(rd.fps),
                str(rd.fps_base),
                "-s",
                str(frame_start),
                "-e",
                str(frame_end),
                "-j",
                str(scene.frame_step),
                file,
            ]
            cmd.extend(opts)
        elif preset == 'DJV':
            opts = [file, "-playback_speed", str(int(fps_final))]
            cmd.extend(opts)
        elif preset == 'FRAMECYCLER':
            opts = [file, f"{scene.frame_start:d}-{scene.frame_end:d}"]
            cmd.extend(opts)
        elif preset == 'RV':
            opts = ["-fps", str(rd.fps), "-play", f"[ {file:s} ]"]
            cmd.extend(opts)
        elif preset == 'MPLAYER':
            opts = []
            if is_movie:
                opts.append(file)
            else:
                opts += [("mf://" + file.replace("#", "?")), "-mf",
                         f"fps={fps_final:4f}"]

            opts += ["-loop", "0", "-really-quiet", "-fs"]
            cmd.extend(opts)
        else:  # 'CUSTOM'
            cmd.append(file)

        # launch it
        print("Executing command:\n ", " ".join(quote(c) for c in cmd))

        # workaround for boost 1.46, can be eventually removed. bug: [#32350]
        env_copy = os.environ.copy()
        if preset == 'INTERNAL':
            env_copy["LC_ALL"] = "C"
        # end workaround

        try:
            subprocess.Popen(cmd, env=env_copy)
        except Exception as e:
            err_msg = tip_(
                "Couldn't run external animation player with command %r\n%s"
            ) % (cmd, e)
            self.report(
                {'ERROR'},
                err_msg,
            )
            return {'CANCELLED'}

        return {'FINISHED'}
Пример #13
0
    def execute(self, _context):
        import traceback
        import zipfile
        import os

        filepath = self.filepath

        path_app_templates = bpy.utils.user_resource(
            'SCRIPTS', os.path.join("startup", "bl_app_templates_user"),
            create=True,
        )

        if not path_app_templates:
            self.report({'ERROR'}, "Failed to get add-ons path")
            return {'CANCELLED'}

        if not os.path.isdir(path_app_templates):
            try:
                os.makedirs(path_app_templates, exist_ok=True)
            except:
                traceback.print_exc()

        app_templates_old = set(os.listdir(path_app_templates))

        # check to see if the file is in compressed format (.zip)
        if zipfile.is_zipfile(filepath):
            try:
                file_to_extract = zipfile.ZipFile(filepath, 'r')
            except:
                traceback.print_exc()
                return {'CANCELLED'}

            if self.overwrite:
                for f in file_to_extract.namelist():
                    module_filesystem_remove(path_app_templates, f)
            else:
                for f in file_to_extract.namelist():
                    path_dest = os.path.join(path_app_templates, os.path.basename(f))
                    if os.path.exists(path_dest):
                        self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
                        return {'CANCELLED'}

            try:  # extract the file to "bl_app_templates_user"
                file_to_extract.extractall(path_app_templates)
            except:
                traceback.print_exc()
                return {'CANCELLED'}

        else:
            # Only support installing zipfiles
            self.report({'WARNING'}, "Expected a zip-file %r\n" % filepath)
            return {'CANCELLED'}

        app_templates_new = set(os.listdir(path_app_templates)) - app_templates_old

        # in case a new module path was created to install this addon.
        bpy.utils.refresh_script_paths()

        # print message
        msg = (
            tip_("Template Installed (%s) from %r into %r") %
            (", ".join(sorted(app_templates_new)), filepath, path_app_templates)
        )
        print(msg)
        self.report({'INFO'}, msg)

        return {'FINISHED'}
Пример #14
0
    def execute(self, context):
        import addon_utils
        import traceback
        import zipfile
        import shutil
        import os

        pyfile = self.filepath

        if self.target == 'DEFAULT':
            # don't use bpy.utils.script_paths("addons") because we may not be able to write to it.
            path_addons = bpy.utils.user_resource('SCRIPTS', "addons", create=True)
        else:
            path_addons = context.preferences.filepaths.script_directory
            if path_addons:
                path_addons = os.path.join(path_addons, "addons")

        if not path_addons:
            self.report({'ERROR'}, "Failed to get add-ons path")
            return {'CANCELLED'}

        if not os.path.isdir(path_addons):
            try:
                os.makedirs(path_addons, exist_ok=True)
            except:
                traceback.print_exc()

        # Check if we are installing from a target path,
        # doing so causes 2+ addons of same name or when the same from/to
        # location is used, removal of the file!
        addon_path = ""
        pyfile_dir = os.path.dirname(pyfile)
        for addon_path in addon_utils.paths():
            if os.path.samefile(pyfile_dir, addon_path):
                self.report({'ERROR'}, "Source file is in the add-on search path: %r" % addon_path)
                return {'CANCELLED'}
        del addon_path
        del pyfile_dir
        # done checking for exceptional case

        addons_old = {mod.__name__ for mod in addon_utils.modules()}

        # check to see if the file is in compressed format (.zip)
        if zipfile.is_zipfile(pyfile):
            try:
                file_to_extract = zipfile.ZipFile(pyfile, 'r')
            except:
                traceback.print_exc()
                return {'CANCELLED'}

            if self.overwrite:
                for f in file_to_extract.namelist():
                    module_filesystem_remove(path_addons, f)
            else:
                for f in file_to_extract.namelist():
                    path_dest = os.path.join(path_addons, os.path.basename(f))
                    if os.path.exists(path_dest):
                        self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
                        return {'CANCELLED'}

            try:  # extract the file to "addons"
                file_to_extract.extractall(path_addons)
            except:
                traceback.print_exc()
                return {'CANCELLED'}

        else:
            path_dest = os.path.join(path_addons, os.path.basename(pyfile))

            if self.overwrite:
                module_filesystem_remove(path_addons, os.path.basename(pyfile))
            elif os.path.exists(path_dest):
                self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
                return {'CANCELLED'}

            # if not compressed file just copy into the addon path
            try:
                shutil.copyfile(pyfile, path_dest)
            except:
                traceback.print_exc()
                return {'CANCELLED'}

        addons_new = {mod.__name__ for mod in addon_utils.modules()} - addons_old
        addons_new.discard("modules")

        # disable any addons we may have enabled previously and removed.
        # this is unlikely but do just in case. bug [#23978]
        for new_addon in addons_new:
            addon_utils.disable(new_addon, default_set=True)

        # possible the zip contains multiple addons, we could disallow this
        # but for now just use the first
        for mod in addon_utils.modules(refresh=False):
            if mod.__name__ in addons_new:
                info = addon_utils.module_bl_info(mod)

                # show the newly installed addon.
                context.window_manager.addon_filter = 'All'
                context.window_manager.addon_search = info["name"]
                break

        # in case a new module path was created to install this addon.
        bpy.utils.refresh_script_paths()

        # print message
        msg = (
            tip_("Modules Installed (%s) from %r into %r") %
            (", ".join(sorted(addons_new)), pyfile, path_addons)
        )
        print(msg)
        self.report({'INFO'}, msg)

        return {'FINISHED'}