try:
import poser
except ImportError:
import _POSER_FAKE as poser
import wx
import wx.lib.agw.flatnotebook as fnb
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.main_panel = wx.Panel(self, size=(5,150))
self.notebook = fnb.FlatNotebook(
self.main_panel,
# fnb.FNB_HIDE_TABS
agwStyle=fnb.FNB_FANCY_TABS|fnb.FNB_NO_X_BUTTON|fnb.FNB_NO_NAV_BUTTONS|fnb.FNB_NO_TAB_FOCUS
)
self.notebook.SetBackgroundColour(wx.Colour(192,192,192))
self.moving_controls = {}
self.create_materiels_page()
self.create_tools_page()
main_sizer = wx.BoxSizer()
main_sizer.Add(self.notebook, 1, wx.ALL | wx.EXPAND, 5)
self.main_panel.SetSizer(main_sizer)
self.Show()
def create_tools_page(self):
self.cmd_panel = wx.Panel(self.notebook)
# Action button
x = 120
y = 100
self.button_export = wx.Button(
self.cmd_panel,
label='Export',
pos=(self.dlg_w-x, self.dlg_h-y),
name='button_export'
)
self.moving_controls[self.button_export] = (x, y)
self.button_export.Bind(wx.EVT_BUTTON, self.action_export)
self.notebook.AddPage(self.cmd_panel, "Tools")
def create_materiels_page(self):
self.HTL_mat_panel = wx.Panel(self.notebook)
self.mats_tree_list = HTL.HyperTreeList(
self.HTL_mat_panel,
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)
# Drop all this in a notebook page
self.notebook.AddPage(self.HTL_mat_panel, "Material List" )
# ---------- 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
title_ok = False
for file_name, values in dict(sorted(full_list.items())).items():
gamma, filtering = values #full_list[file_name]
check = True
if not title_ok:
title_ok = True
self.txt_material_list.append(material.Name())
self.txt_material_list.append(f" {file_name}")
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())
self.txt_material_list = []
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.txt_material_list = [title]
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()
def action_export(self, event=None):
if not event: return
if not self.txt_material_list:
poser.DialogSimple.MessageBox("Nothing...")
return
get_open_file = poser.DialogFileChooser(
poser.kDialogFileChooserSave,
self.figure.Name(),
"Select output file",
wildcard="Text files (*.txt)|*.txt"
)
if (get_open_file.Show()):
output = open(get_open_file.Path(), 'w')
for line in self.txt_material_list:
print(line, file=output)
output.close()
# ---------- 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)
The result is this:
I know that the poor lonely button seems somewhat lost in its low right corner but I'm used to this kind of dialog setup...
Change the size of the window and it will follow your mouse.