Forum: Poser Python Scripting


Subject: List of bitmaps used by a figure/prop

Y-Phil opened this issue on Jan 01, 2024 ยท 11 posts


Y-Phil posted Tue, 02 January 2024 at 10:02 AM

Ok, here is a version that runs on Poser12 and Poser13.

Even though I have avoided any Python3-specific instructions, I have not been able to instantiate the HyperTreeList control, something's making the original code crash on Poser11.
I could have used a simple list but the fact that it's hierarchically presented is one of the key point.
The window may remain opened, and pushed on a side of the screen. Each time the selection is done on another object and the window is once again activated, it is refreshed. Once the window is closed using its upper-right cross, it maintains its position and size for the next time.

Furthermore, that are probably a few useless instructions but consider that it's a small part (260 lines) among a rather bigger Python script (more than 3130 lines) 

try:
    import poser
except ImportError:
    import _POSER_FAKE as poser

import wx
import wx.lib.agw.hypertreelist as HTL
import os
import os.path
import configparser

HTL_ITEM_NORMAL=0
HTL_ITEM_CHECKBOX=1
HTL_ITEM_RADIO=2

class CypMaterialsList(wx.Frame):

    # ---------- Dialog part

    def __init__(self, dlg_name):
        self.parent = poser.WxAuiManager().GetManagedWindow().GetParent()
        self.title_font = wx.Font(9, wx.DECORATIVE, wx.NORMAL, wx.BOLD)
        self.dlg_name = dlg_name

        self.load_config()
        self.load_environment()

        wx.Frame.__init__(
            self,
            self.parent,
            title=self.get_dialog_title(),
            pos=(self.dlg_x, self.dlg_y),
            size=(self.dlg_w, self.dlg_h),
            style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_TOOL_WINDOW|wx.FRAME_FLOAT_ON_PARENT|wx.FRAME_NO_TASKBAR|wx.FRAME_DRAWER,
            name=dlg_name
        )

        self.Bind(wx.EVT_CLOSE, self.on_close)
        self.Bind(wx.EVT_ACTIVATE, self.on_activate)
        self.Bind(wx.EVT_SIZING, self.on_sizing)

        self.HTL_mat_panel = wx.Panel(self)

        self.create_materiels_list()

        self.Show()

    def create_materiels_list(self):
        htl_width = self.dlg_w-500
        self.mats_tree_list = HTL.HyperTreeList(
            self.HTL_mat_panel,
            pos=(5,5),
            size=(htl_width, self.dlg_h-45),
            agwStyle=wx.TR_DEFAULT_STYLE
        )

        self.mats_tree_list.AddColumn("Materials")
        self.mats_tree_list.AddColumn("Gamma")
        self.mats_tree_list.AddColumn("Filtering")
        self.mats_tree_root = self.mats_tree_list.AddRoot("Root")
        self.mats_tree_root.Expand()
        self.tree_root_materials = self.mats_tree_list.AppendItem(self.mats_tree_root, "Object", ct_type=HTL_ITEM_NORMAL)
        self.tree_root_materials.Expand()
        self.mats_tree_list.SetColumnWidth(0, int(self.dlg_w*0.70))

        self.HTL_Prepare_materials_list()

        sizer = wx.BoxSizer()
        sizer.Add(self.mats_tree_list, 1, wx.ALL | wx.EXPAND, 5)
        self.HTL_mat_panel.SetSizer(sizer)

    # ---------- HyperTreeList mamagenemt

    def _deep_scan_for_bitmaps(self, from_node=None):
        if not from_node: return {}

        current_list = {}
        detected = {}
        if from_node.Type() == 'image_map':
            sni = from_node.InputByInternalName('Image_Source')
            file_name = sni.Value()
            texture = sni.Texture()
            gamma = 2.2 if texture.UseSceneGamma() else texture.Gamma()
            filtering_i = int(from_node.InputByInternalName('Filtering').Value())
            filtering = "Filtering: " + { 1:'none', 2:'fast', 3:'quality', 4:'crisp'}.get(filtering_i, 'unknown')
            detected = {file_name: (str(gamma), filtering)}

        elif from_node.Type() == 'ccl_ImageTexture':
            sni = from_node.InputByInternalName('Image')
            file_name = sni.Value()
            texture = sni.Texture()
            gamma = 2.2 if texture.UseSceneGamma() else texture.Gamma()
            detected = {file_name: (str(gamma), "-")}

        if not detected:
            for sni in from_node.Inputs():
                in_node = sni.InNode()
                if in_node:
                    detected = self._deep_scan_for_bitmaps(from_node=in_node)
                    if detected:
                        current_list.update(detected)

        else:
            current_list.update(detected)

        return current_list

    def OnCompareItems(self, item1, item2):
        a = item1.Name()
        b = item2.Name()
        return (a > b) - (a < b)  # equivalent to old cmp function

    def HTL_mat_scan_4_bitmaps(self, material, place):
        tree = material.ShaderTree()
        root_node = tree.RendererRootNode(self.scene.CurrentRenderEngine())
        full_list = self._deep_scan_for_bitmaps(from_node=root_node)

        check = False
        for file_name, values in dict(sorted(full_list.items())).items():
            gamma, filtering = values #full_list[file_name]
            check = True
            img_info = self.mats_tree_list.AppendItem(place, file_name)
            img_info.SetText(1, gamma)
            img_info.SetText(2, filtering)

        return check

    def HTL_mats_scan(self, place, item):
        if not item.IsProp() and not item.IsBodyPart(): return

        sorted_materials = [
            material
            for material in item.Materials()
        ]
        from functools import cmp_to_key
        sorted_materials.sort(key=cmp_to_key(self.OnCompareItems))
        for material in sorted_materials:
            this = self.mats_tree_list.AppendItem(place, material.Name())
            if not self.HTL_mat_scan_4_bitmaps(material, this):
                this.Hide(True)
            else:
                this.Expand()

    def HTL_Prepare_materials_list(self):
        self.tree_root_materials.DeleteChildren(self.mats_tree_list.GetMainWindow())

        title = "Object"
        if self.figure:
            if self.figure.IsFigure():
                actor = self.figure.RootActor()
                title = "Figure: {}".format(self.figure.Name())
            else:
                actor = self.figure
                title = "{}: {}".format(title, actor.Name())

            self.HTL_mats_scan(self.tree_root_materials, actor)

        self.tree_root_materials.SetText(0, title)

    # ---------- Events management

    def on_close(self, event=None):
        try:
            if self.IsIconized(): self.Iconize(False)
            if self.IsMaximized(): self.Maximize(False)
            if not self.IsIconized():
                self.save_config()
        except: pass

        self.Destroy()

    def on_sizing(self, event=None):
        # if event:
        #     new_w, new_h = event.GetSize()
        #     for ctrl, rel_pos in self.moving_controls.items():
        #         ctrl.Move(new_w-1-rel_pos[0], new_h-1-rel_pos[1])

        event.ResumePropagation(wx.EVENT_PROPAGATE_MAX)

    def on_activate(self, event=None):
        if not event: return
        if event.GetActive():
            self.load_environment()
            self.SetTitle(self.get_dialog_title())

            # ---------- Hypertreelists Management

            check = getattr(self, 'mats_tree_root', None)
            if check:
                self.HTL_Prepare_materials_list()

    # ---------- Gen Tools

    def get_render_engine(self):
        try:
            return {
                4: " - Superfly",
                1: " - Firefly",
                3: " - Sketch",
                0: " - Preview"
            }.get(self.scene.CurrentRenderEngine(), "")
        except:
            return ''

    def load_config(self, def_x=100, def_y=200, def_w=750, def_h=300):
        '''
            Load the dialog's position from the configuration file, if any
        '''
        self.section_name = "Materials List"

        self.configFile = os.path.join(poser.PrefsLocation(), "Cyp" + os.extsep + "cfg")
        self.config = configparser.RawConfigParser()
        self.config.read(self.configFile)
        if not self.config.has_section(self.section_name): self.config.add_section(self.section_name)

        try: self.dlg_x = self.config.getint(self.section_name, "x")
        except: self.dlg_x = def_x
        try: self.dlg_y = self.config.getint(self.section_name, "y")
        except: self.dlg_y = def_y
        try: self.dlg_w = self.config.getint(self.section_name, "w")
        except: self.dlg_w = def_w
        try: self.dlg_h = self.config.getint(self.section_name, "h")
        except: self.dlg_h = def_h

    def save_config(self):
        r = self.GetScreenRect()

        self.config.set(self.section_name, "x", r.left)
        self.config.set(self.section_name, "y", r.top)
        self.config.set(self.section_name, "w", r.width)
        self.config.set(self.section_name, "h", r.height)
        self.config.write(open(self.configFile, "w"))

    def load_environment(self):
        '''
            Load all values for the dialog,
        '''
        self.scene = poser.Scene()
        try:
            figure = self.scene.CurrentActor()
            if figure.IsBodyPart():
                figure = self.scene.CurrentFigure()
        except:
            figure = None

        self.figure = figure

    def get_dialog_title(self):
        title = "List of materials {}".format(self.get_render_engine())
        if self.figure:
            title = "{}: {}".format(title, self.figure.Name())

        return title


dlg_name = "Cyp's Materials List"
# Kill any stalled previous instances
[w.Destroy() for w in poser.WxAuiManager().GetManagedWindow().GetParent().Children if w.GetName()==dlg_name]
CypMaterialsList(dlg_name)

๐’ซ๐’ฝ๐“Ž๐“


(ใฃโ—”โ—กโ—”)ใฃ

๐Ÿ‘ฟ Win11 on i9-13900K@5GHz, 64GB, RoG Strix B760F Gamng, Asus Tuf Gaming RTX 4070 OC Edition, 1 TB SSD, 6+4+8TB HD
๐Ÿ‘ฟ Mac Mini M2, Sequoia 15.2, 16GB, 500GB SSD
๐Ÿ‘ฟ Nas 10TB
๐Ÿ‘ฟ Poser 13 and soon 14 โค๏ธ