Skip to content
Snippets Groups Projects
scripting_gui.py 8.07 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    # <pep8 compliant>
    """User interface to POV Scene Description Language snippets or full includes:
    
    import, load, create or edit """
    
    import bpy
    from bpy.utils import register_class, unregister_class
    from bpy.types import Operator, Menu, Panel
    from sys import platform  # really import here, as in ui.py and in render.py?
    import os  # really import here and in render.py?
    from os.path import isfile
    
    
    def locate_docpath():
        """POV can be installed with some include files.
    
        Get their path as defined in user preferences or registry keys for
        the user to be able to invoke them."""
    
        addon_prefs = bpy.context.preferences.addons[__package__].preferences
        # Use the system preference if its set.
        pov_documents = addon_prefs.docpath_povray
        if pov_documents:
            if os.path.exists(pov_documents):
                return pov_documents
            # Implicit else, as here return was still not triggered:
            print(
                "User Preferences path to povray documents %r NOT FOUND, checking $PATH" % pov_documents
            )
    
        # Windows Only
        if platform.startswith('win'):
            import winreg
    
            try:
                win_reg_key = winreg.OpenKey(
                    winreg.HKEY_CURRENT_USER, "Software\\POV-Ray\\v3.7\\Windows"
                )
                win_docpath = winreg.QueryValueEx(win_reg_key, "DocPath")[0]
                pov_documents = os.path.join(win_docpath, "Insert Menu")
                if os.path.exists(pov_documents):
                    return pov_documents
            except FileNotFoundError:
                return ""
        # search the path all os's
        pov_documents_default = "include"
    
        os_path_ls = os.getenv("PATH").split(':') + [""]
    
        for dir_name in os_path_ls:
            pov_documents = os.path.join(dir_name, pov_documents_default)
            if os.path.exists(pov_documents):
                return pov_documents
        return ""
    
    
    
    # ---------------------------------------------------------------- #
    
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    class TextButtonsPanel:
        """Use this class to define buttons from the side tab of
        text window."""
    
        bl_space_type = 'TEXT_EDITOR'
        bl_region_type = 'UI'
        bl_label = "POV-Ray"
        COMPAT_ENGINES = {'POVRAY_RENDER'}
    
        @classmethod
        def poll(cls, context):
            text = context.space_data
            rd = context.scene.render
            return text and (rd.engine in cls.COMPAT_ENGINES)
    
    
    
    # ---------------------------------------------------------------- #
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    # Text Povray Settings
    
    # ---------------------------------------------------------------- #
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    
    class TEXT_OT_POV_insert(Operator):
    
        """Create blender text editor operator to insert pov snippets like other pov IDEs"""
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = "text.povray_insert"
        bl_label = "Insert"
    
        filepath: bpy.props.StringProperty(name="Filepath", subtype='FILE_PATH')
    
        @classmethod
        def poll(cls, context):
            text = context.space_data.text
            return context.area.type == 'TEXT_EDITOR' and text is not None
            # return bpy.ops.text.insert.poll() this Bpy op has no poll()
    
        def execute(self, context):
            if self.filepath and isfile(self.filepath):
    
                with open(self.filepath, "r") as file:
                    bpy.ops.text.insert(text=file.read())
    
                    # places the cursor at the end without scrolling -.-
                    # context.space_data.text.write(file.read())
                if not file.closed:
                    file.close()
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            return {'FINISHED'}
    
    
    def validinsert(ext):
        """Since preview images could be in same folder, filter only insertable text"""
        return ext in {".txt", ".inc", ".pov"}
    
    
    class TEXT_MT_POV_insert(Menu):
    
        """Create a menu launcher in text editor for the TEXT_OT_POV_insert operator ."""
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_label = "Insert"
        bl_idname = "TEXT_MT_POV_insert"
    
        def draw(self, context):
            pov_documents = locate_docpath()
            prop = self.layout.operator("wm.path_open", text="Open folder", icon='FILE_FOLDER')
            prop.filepath = pov_documents
            self.layout.separator()
    
            # todo: structure submenus by dir
            pov_insert_items_list = [root for root, dirs, files in os.walk(pov_documents)]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            print(pov_insert_items_list)
            self.path_menu(
                pov_insert_items_list,
                "text.povray_insert",
                # {"internal": True},
                filter_ext=validinsert,
            )
    
    
    class TEXT_PT_POV_custom_code(TextButtonsPanel, Panel):
        """Use this class to create a panel in text editor for the user to decide if he renders text
    
        only or adds to 3d scene."""
    
        bl_label = "POV"
        COMPAT_ENGINES = {'POVRAY_RENDER'}
    
        def draw(self, context):
            layout = self.layout
    
            text = context.space_data.text
    
            pov_documents = locate_docpath()
            if not pov_documents:
                layout.label(text="Please configure ", icon="INFO")
                layout.label(text="default pov include path ")
                layout.label(text="in addon preferences")
                # layout.separator()
                layout.operator(
                    "preferences.addon_show",
                    text="Go to Render: Persistence of Vision addon",
                    icon="PREFERENCES",
                ).module = "render_povray"
    
                # layout.separator()
            else:
                # print(pov_documents)
                layout.menu(TEXT_MT_POV_insert.bl_idname)
    
            if text:
                box = layout.box()
                box.label(text='Source to render:', icon='RENDER_STILL')
                row = box.row()
                row.prop(text.pov, "custom_code", expand=True)
                if text.pov.custom_code in {'3dview'}:
                    box.operator("render.render", icon='OUTLINER_DATA_ARMATURE')
                if text.pov.custom_code in {'text'}:
                    rtext = bpy.context.space_data.text  # is r a typo ? or why written, not used
                    box.operator("text.run", icon='ARMATURE_DATA')
                # layout.prop(text.pov, "custom_code")
                elif text.pov.custom_code in {'both'}:
                    box.operator("render.render", icon='POSE_HLT')
                    layout.label(text="Please specify declared", icon="INFO")
                    layout.label(text="items in properties ")
                    # layout.label(text="")
                    layout.label(text="replacement fields")
    
    
    
    # ---------------------------------------------------------------- #
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    # Text editor templates from header menu
    
    
    class TEXT_MT_POV_templates(Menu):
        """Use this class to create a menu for the same pov templates scenes as other pov IDEs."""
    
        bl_label = "POV"
    
        # We list templates on file evaluation, we can assume they are static data,
        # and better avoid running this on every draw call.
        template_paths = [os.path.join(os.path.dirname(__file__), "templates_pov")]
    
        def draw(self, context):
            self.path_menu(self.template_paths, "text.open", props_default={"internal": True})
    
    
    def menu_func_templates(self, context):
        """Add POV files to the text editor templates menu"""
        # Do not depend on POV being active renderer here...
        self.layout.menu("TEXT_MT_POV_templates")
    
    
    
    # ---------------------------------------------------------------- #
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    # POV Import menu
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    class VIEW_MT_POV_import(Menu):
        """Use this class for the import menu."""
    
        bl_idname = "POVRAY_MT_import_tools"
        bl_label = "Import"
    
        def draw(self, context):
            layout = self.layout
            layout.operator_context = 'INVOKE_REGION_WIN'
            layout.operator("import_scene.pov", icon="FORCE_LENNARDJONES")
    
    
    def menu_func_import(self, context):
        """Add the import operator to menu"""
        engine = context.scene.render.engine
        if engine == 'POVRAY_RENDER':
            self.layout.operator("import_scene.pov", icon="FORCE_LENNARDJONES")
    
    
    classes = (
        VIEW_MT_POV_import,
        TEXT_OT_POV_insert,
        TEXT_MT_POV_insert,
        TEXT_PT_POV_custom_code,
        TEXT_MT_POV_templates,
    )
    
    
    def register():
        for cls in classes:
            register_class(cls)
    
        bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
        bpy.types.TEXT_MT_templates.append(menu_func_templates)
    
    
    def unregister():
        bpy.types.TEXT_MT_templates.remove(menu_func_templates)
        bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
    
        for cls in reversed(classes):
            unregister_class(cls)