1 from __future__ import print_function 2 3 try: 4 import poser 5 except ImportError: 6 raise RuntimeError("Script must run in Poser.") 7 8 import wx 9 import wx.aui 10 import sys 11 import os 12 import time 13 import json 14 import numpy as NP 15 16 CONFIG = dict() 17 BASEPATH = os.path.abspath(os.path.dirname(sys.argv[0])) 18 SCRIPT_FILENAME = os.path.basename(sys.argv[0]) 19 CONFIG_FILENAME = SCRIPT_FILENAME.rsplit(".")[0] + ".cfg" 20 21 # Forced floatingpoint precision. Mainly to help avoiding floatingpoint 22 # errors while reading files from external modelers. 23 PRECISION = 8 24 NP_PRECISION = NP.float32 25 26 _LF = chr(10) 27 28 def ErrDialog(err, msg=None): 29 dlg = wx.MessageDialog(None, caption=err, message=msg, style=wx.ICON_ERROR) 30 dlg.ShowModal() 31 dlg.Close() 32 33 34 _INFODISPLAY = None 35 36 37 def InfoDisplay(parent, msg, time2display=1000): 38 global _INFODISPLAY 39 if _INFODISPLAY is None: 40 _INFODISPLAY = wx.Dialog(parent, 41 style=wx.STAY_ON_TOP | wx.DIALOG_NO_PARENT) 42 43 _INFODISPLAY.Center() 44 45 46 def read_config(): 47 global CONFIG 48 fname = os.path.join(BASEPATH, CONFIG_FILENAME) 49 if os.path.isfile(fname): 50 try: 51 with open(fname, "r") as fh: 52 CONFIG = json.load(fh, encoding="utf-8") 53 except IOError: 54 pass 55 56 57 def write_config(): 58 global CONFIG 59 fname = os.path.join(BASEPATH, CONFIG_FILENAME) 60 try: 61 with open(fname, "w") as fh: 62 json.dump(CONFIG, fh) 63 except IOError: 64 ErrDialog("File Error.", "Can't write configfile '{}.".format(fname)) 65 66 67 def find_parms_with_name(parmname): 68 """ 69 Return dict with actornames as key an parameter as content. 70 :param parmname: 71 :return: 72 """ 73 assert isinstance(parmname, basestring) 74 actor_dict = dict() 75 for actor in poser.Scene().Actors(): 76 ac_name = actor.Name() 77 for parm in actor.Parameters(): 78 if parm.Name() == parmname: 79 actor_dict[ac_name] = parm 80 81 return actor_dict 82 83 84 def write_matfile(filename, materials): 85 """ 86 Write out a simple material-file. 87 """ 88 try: 89 tmp = open(filename, "w") 90 tmp.close() 91 except IOError: 92 return ErrDialog("File Error.", "Can't create or write to file '{}'." + _LF + 93 "Make sure directory '{}' exist and is writable.". 94 format(filename, os.path.dirname(filename))) 95 96 with open(filename, "w") as mfh: 97 for mat in materials: 98 print("newmtl", mat.Name(), file=mfh) 99 print("Ns", mat.Ns(), file=mfh) 100 print("Ka", "0 0 0", file=mfh) 101 print("Kd", " ".join(map(str, mat.DiffuseColor())), file=mfh) 102 print("Ks", "0 0 0", file=mfh) 103 if mat.TextureMapFileName(): 104 print("map_Kd", mat.TextureMapFileName(), file=mfh) 105 if mat.BumpMapFileName(): 106 print("map_Bump", mat.BumpMapFileName(), file=mfh) 107 108 109 def collect_geometry(figure): 110 if figure is None: 111 return None 112 np_vertex = lambda v: NP.array((v.X(), v.Y(), v.Z()), NP_PRECISION) 113 geom, actorlist, actor_indices = figure.UnimeshInfo() 114 verts = NP.zeros((geom.NumVertices(), 3), NP_PRECISION) 115 116 for actor_idx, actor in enumerate(actorlist): 117 world_verts = actor.Geometry().WorldVertices() 118 for i, vertex_idx in enumerate(actor_indices[actor_idx]): 119 verts[vertex_idx] = np_vertex(world_verts[i]) 120 121 return dict(vertices=verts, 122 geom=geom, 123 actorlist=actorlist, 124 actor_indices=actor_indices) 125 126 127 def read_vertices(filename): 128 """ 129 Read Wavefront obj-file saved to file. Typically a figure exported 130 from Poser and modified with an external modeller (Blender etc). 131 """ 132 vertices = list() 133 try: 134 with open(filename, "r") as fh: 135 for line in fh: 136 if not line: 137 break 138 c, _, v = line.strip().partition(" ") 139 if c == "v": 140 vertices.append(map(float, v.split())) 141 142 # Remove following line if vertices are not in one block, 143 # so the whole file is processed. 144 elif c in ("vt", "vn", "f"): 145 break 146 except IndexError: 147 return ErrDialog("Vertex Error.", 148 "Vertice in file '%filename' corrupted.") 149 except IOError: 150 return ErrDialog("File Error.", 151 "File '{}' does not exist or is not accessible.". 152 format(filename)) 153 154 return NP.array(vertices, NP_PRECISION) 155 156 157 def do_export(figure, onFinish=None): 158 """ 159 Export figure to Wavefront obj file. 160 """ 161 assert isinstance(figure, poser.FigureType) 162 figurename = figure.Name() 163 parms = collect_geometry(figure) 164 vertices = parms["vertices"] 165 geom = parms["geom"] 166 use_material = CONFIG.get("CTRL_ExportTexture", True) 167 use_groups = CONFIG.get("CTRL_ExportGroups", False) 168 morphname = CONFIG["CTRL_MorphName"].strip() 169 vertices *= int(CONFIG.get("Scale", 100)) 170 171 if CONFIG.get("CTRL_SingleSelectFile", True): 172 173 with wx.FileDialog(None, "Export Wavefront file", 174 style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT | wx.FD_CHANGE_DIR, 175 defaultDir=CONFIG.get("ExportPath", BASEPATH), 176 defaultFile="{}-{}.obj".format(figurename, morphname) 177 ) as dlg: 178 if dlg.ShowModal() == wx.ID_CANCEL: 179 return 180 181 CONFIG["ExportPath"] = os.path.dirname(dlg.GetPath()) 182 filename = dlg.GetFilename() 183 else: 184 filename = os.path.join(CONFIG["ExportPath"], "{}-{}.obj".format(figurename, morphname)) 185 186 try: 187 tmp = open(filename, "w") 188 tmp.close() 189 except IOError: 190 ErrDialog("Can't create or write to file '{}'.", 191 "Maybe you have to select another directory first.") 192 return 193 194 with open(filename, "w") as fh: 195 print("### Date : %s" % time.asctime(), file=fh) 196 print("### Figure : %s" % figurename, file=fh) 197 print("### Vertices: %s" % len(vertices), file=fh) 198 199 if use_material and geom.Materials(): 200 matfile = filename.rsplit(".", 1)[0] + ".mtl" 201 write_matfile(matfile, geom.Materials()) 202 print("mtllib ./" + os.path.basename(matfile), file=fh) 203 204 for vertex in vertices: 205 print("v {} {} {}".format(*vertex), file=fh) 206 207 if use_material: 208 for tvert in geom.TexVertices(): 209 print("vt {} {}".format(tvert.U(), tvert.V()), file=fh) 210 211 current_groups = list() 212 current_mat = list() 213 if not use_groups: 214 print("g", figurename, file=fh) 215 216 polys = geom.Polygons() 217 tpolys = geom.TexPolygons() 218 sets = geom.Sets() 219 tsets = geom.TexSets() 220 221 for index, poly in enumerate(polys): 222 if use_groups: 223 if poly.Groups() != current_groups: 224 current_groups = poly.Groups() 225 print("g", ", ".join(current_groups), file=fh) 226 227 if use_material: 228 if poly.MaterialName() != current_mat: 229 current_mat = poly.MaterialName() 230 print("usemtl", current_mat, file=fh) 231 232 line = [str(sets[idx + poly.Start()] + 1) for idx in range(poly.NumVertices())] 233 if use_material: 234 tpoly = tpolys[index] 235 for tidx, v in enumerate((tsets[idx + tpoly.Start()] + 1) for idx in range(tpoly.NumTexVertices())): 236 line[tidx] += "/%d" % v 237 238 print("f", " ".join(map(str, line)), file=fh) 239 240 CONFIG["LastExported"] = filename 241 if onFinish is not None: 242 onFinish() 243 return filename 244 245 246 def do_import(figure, morphname): 247 assert isinstance(figure, poser.FigureType) 248 figurename = figure.Name() 249 geom, actorlist, actor_indices = figure.UnimeshInfo() 250 251 if CONFIG.get("CTRL_SingleSelectFile", True): 252 old_filename = CONFIG.get("LastExported", None) 253 if not old_filename: 254 with wx.FileDialog(None, "Import Original exported Wavefront file", 255 style=wx.FD_OPEN | wx.FD_CHANGE_DIR | wx.FD_FILE_MUST_EXIST, 256 defaultDir=CONFIG.get("ImportPath", BASEPATH), 257 defaultFile="{}-{}.obj".format(figurename, morphname) 258 ) as dlg: 259 if dlg.ShowModal() == wx.ID_CANCEL: 260 return 261 old_filename = dlg.GetFilename() 262 263 with wx.FileDialog(None, "Import Wavefront file as morph", 264 style=wx.FD_OPEN | wx.FD_CHANGE_DIR | wx.FD_FILE_MUST_EXIST, 265 defaultDir=CONFIG.get("ImportPath", BASEPATH), 266 defaultFile="{}-{}_mod.obj".format(figurename, morphname) 267 ) as dlg: 268 if dlg.ShowModal() == wx.ID_CANCEL: 269 return 270 271 CONFIG["ImportPath"] = os.path.dirname(dlg.GetPath()) 272 new_filename = dlg.GetFilename() 273 else: 274 old_filename = os.path.join(CONFIG["ExportPath"], "{}-{}.obj".format(figurename, morphname)) 275 new_filename = os.path.join(CONFIG["ImportPath"], "{}-{}_mod.obj".format(figurename, morphname)) 276 277 verts_new = read_vertices(new_filename) 278 verts_old = read_vertices(old_filename) 279 if len(verts_old) != len(verts_new): 280 ErrDialog("Vertices mismatch.", "Old number of vertices: {}." 281 "New number of vertices: {}.". 282 format(len(verts_old), len(verts_new))) 283 return 284 285 vertices = (verts_new - verts_old) / int(CONFIG.get("Scale", 100)) 286 del verts_new 287 del verts_old 288 289 body = figure.ParentActor() 290 masterdial = body.Parameter(morphname) 291 if masterdial is None: 292 body.CreateValueParameter(morphname) 293 masterdial = body.Parameter(morphname) 294 if masterdial is None: 295 return ErrDialog("Morph Error.", "Can't find or create morph in body actor.") 296 297 for actor_idx, actor in enumerate(actorlist): 298 morph = list() 299 for i, v_idx in enumerate(actor_indices[actor_idx]): 300 x, y, z = map(lambda a: round(a, PRECISION), vertices[v_idx]) 301 if x != 0 or y != 0 or z != 0: 302 morph.append((i, x, y, z)) 303 304 if len(morph) == 0: 305 continue 306 307 morphparm = actor.Parameter(morphname) 308 if morphparm is None: 309 actor.SpawnTarget(morphname) 310 morphparm = actor.Parameter(morphname) 311 312 if morphparm is None: 313 return ErrDialog("Morph Error", "Can't create Morphtarget.") 314 if not morphparm.IsMorphTarget(): 315 return ErrDialog("Morph error.", "Morph Parametername ('%s')" + _LF + 316 "already exist but is not a morph" 317 % morphname) 318 for i in range(actor.Geometry().NumVertices()): 319 morphparm.SetMorphTargetDelta(i, 0, 0, 0) 320 for i, x, y, z in morph: 321 morphparm.SetMorphTargetDelta(i, x, y, z) 322 323 while morphparm.NumValueOperations(): 324 morphparm.DeleteValueOperation(0) 325 morphparm.AddValueOperation(poser.kValueOpTypeCodeKEY, masterdial) 326 vop = morphparm.ValueOperations()[0] 327 vop.InsertKey(0, 0) 328 vop.InsertKey(1, 1) 329 330 masterdial.SetMinValue(-.5) 331 masterdial.SetMaxValue(1.0) 332 333 334 APP_NAME = "Figure Importer/Exporter" 335 STD_COLOR = 120, 120, 120 336 LBL_COLOR = 80, 80, 80 337 BG_COLOR = 75, 75, 75 338 HL_COLOR = 100, 100, 150 339 FG_COLOR = 200, 200, 200 340 TX_COLOR = 0xfe, 0xfe, 0xfe 341 STOPP_UPDATE_UI = False 342 343 344 class GridBagManager(object): 345 __slots__ = "parent", "sizer", "flag", "border", "current_row", "font" 346 347 def __init__(self, *args, **kwargs): 348 for varname in self.__slots__: 349 setattr(self, varname, kwargs.get(varname, None)) 350 351 for idx, entry in enumerate(args): 352 if self.__slots__[idx] not in kwargs: 353 setattr(self, self.__slots__[idx], entry) 354 355 self.current_row = 0 356 357 assert isinstance(self.parent, wx.Panel) 358 assert isinstance(self.sizer, wx.GridBagSizer) 359 assert isinstance(self.flag, int) 360 assert isinstance(self.border, int) 361 362 def addrow(self, *args, **kwargs): 363 row = int(kwargs.get("row", self.current_row)) 364 col = int(kwargs.get("startcol", 0)) 365 flag = kwargs.get("flag", self.flag) 366 font = kwargs.pop("font", self.font) 367 widgets = [] 368 369 for idx, widget in enumerate(args): 370 if font: widget.SetFont(font) 371 372 self.sizer.Add(widget, pos=(row, col + idx), 373 flag=flag, 374 border=self.border) 375 376 widgets.append(widget) 377 378 self.current_row += 1 379 return widgets 380 381 382 def setTooltip(ctrl, text): 383 if text and ctrl: 384 t = wx.ToolTip(text) 385 t.SetAutoPop(5000) 386 ctrl.SetToolTip(t) 387 return ctrl 388 389 390 def ColoredCtrl(ctrl, **kwargs): 391 ctrl.SetBackgroundColour(kwargs.pop("bgcolor", BG_COLOR)) 392 ctrl.SetForegroundColour(kwargs.pop("fgcolor", FG_COLOR)) 393 return setTooltip(ctrl, kwargs.pop("tooltip", None)) 394 395 396 def LabelCtrl(parent, **kwargs): 397 bg_color = kwargs.pop("bgcolor", BG_COLOR) 398 fg_color = kwargs.pop("fgcolor", FG_COLOR) 399 font = kwargs.pop("font", SYS_FONT) 400 tooltip = kwargs.pop("tooltip", None) 401 ctrl = wx.StaticText(parent, **kwargs) 402 ctrl.SetFont(font) 403 ctrl.SetBackgroundColour(bg_color) 404 ctrl.SetForegroundColour(fg_color) 405 return setTooltip(ctrl, tooltip) 406 407 408 def PrepCtrl(parent, ctrl, **kwargs): 409 bind = kwargs.pop("bind", []) 410 fgcolor = kwargs.pop("fgcolor", FG_COLOR) 411 bgcolor = kwargs.pop("bgcolor", BG_COLOR) 412 font = kwargs.pop("font", None) 413 value = kwargs.pop("value", None) 414 tooltip = kwargs.pop("tooltip", None) 415 416 ctrl = ctrl(parent, **kwargs) 417 418 if bind: 419 if isinstance(bind[0], (tuple, list)): 420 for bind_entry in bind: 421 assert len(bind_entry) == 2 422 ctrl.Bind(bind_entry[0], bind_entry[1]) 423 else: 424 assert len(bind) == 2 425 ctrl.Bind(bind[0], bind[1]) 426 427 ctrl.SetForegroundColour(fgcolor) 428 ctrl.SetBackgroundColour(bgcolor) 429 if font: 430 ctrl.SetFont(font) 431 if value: 432 ctrl.SetValue(value) 433 return setTooltip(ctrl, tooltip) 434 435 436 class AppPanel(wx.Panel): 437 _CONFIGCHANGED = False 438 439 def __init__(self, parent, 440 name="Fullbody Morphs", 441 style=wx.DEFAULT, 442 size=(270, 400), 443 **kwargs): 444 super(self.__class__, self).__init__(parent=parent, 445 id=wx.ID_ANY, 446 name=name, 447 style=style, 448 size=size, 449 **kwargs) 450 read_config() 451 self.stdfont = SYS_FONT 452 453 self.gb_manager = GridBagManager(parent=self, 454 sizer=wx.GridBagSizer(0, 0), 455 flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 456 border=2 457 ) 458 459 self.figurelist = PrepCtrl(self, wx.Choice, choices=[], 460 style=wx.CB_SORT | wx.NO_BORDER, 461 size=(80, 20), name="figurelist", 462 font=self.stdfont.SetPointSize(8), 463 tooltip="Right click to reload Figurelist.", 464 bind=((wx.EVT_RIGHT_UP, self.updateFigurelist), 465 (wx.EVT_CHOICE, self.onChoice)) 466 ) 467 self.updateFigurelist() 468 469 self.initUI(self.gb_manager) 470 471 self.Bind(wx.EVT_WINDOW_DESTROY, self.onClose) 472 # self.Bind(wx.EVT_MOUSE_EVENTS, lambda ev: ev.StopPropagation()) 473 self.Bind(wx.EVT_SIZE, self.onSize) 474 self.updateUI() 475 self.Layout() 476 477 def initUI(self, gbm): 478 assert isinstance(gbm, GridBagManager) 479 self.SetBackgroundColour(BG_COLOR) 480 self.SetForegroundColour(FG_COLOR) 481 self.SetFont(self.stdfont) 482 btn_size = (80, 15) 483 484 gbm.addrow(PrepCtrl(self, wx.Button, label="Export Figure", 485 name="BTN_ExportFigure", 486 style=wx.BORDER_NONE | wx.BU_EXACTFIT, size=(50, 30), 487 bgcolor=STD_COLOR, fgcolor=(0xD0, 0xC0, 0x30), 488 tooltip="Export currently selected figure.", 489 bind=((wx.EVT_LEFT_UP, self.onExportModel), 490 (wx.EVT_ENTER_WINDOW, self.onEnter_button), 491 (wx.EVT_LEAVE_WINDOW, self.onLeave_button)) 492 ), 493 PrepCtrl(self, wx.Button, label="Import Morph", 494 name="BTN_ImportMorph", 495 style=wx.BORDER_NONE | wx.BU_EXACTFIT, size=(50, 30), 496 bgcolor=STD_COLOR, fgcolor=(0xD0, 0xC0, 0x30), 497 tooltip="Import modified figure as morph.", 498 bind=((wx.EVT_LEFT_UP, self.onImportModel), 499 (wx.EVT_ENTER_WINDOW, self.onEnter_button), 500 (wx.EVT_LEAVE_WINDOW, self.onLeave_button)) 501 )) 502 503 gbm.addrow(LabelCtrl(self, label="Figure", 504 fgcolor=STD_COLOR, name="LBL_FigureList"), 505 self.figurelist) 506 507 gbm.addrow(LabelCtrl(self, label="Morphname", 508 fgcolor=STD_COLOR, bgcolor=BG_COLOR), 509 ColoredCtrl(wx.TextCtrl(self, value=CONFIG.get("CTRL_MorphName", "Morph"), 510 name="CTRL_MorphName", style=wx.BORDER_SIMPLE), 511 bgcolor=BG_COLOR, fgcolor=TX_COLOR)) 512 513 gbm.addrow(LabelCtrl(self, label="Increment Morph" + _LF + "on each import", 514 fgcolor=STD_COLOR, bgcolor=BG_COLOR), 515 PrepCtrl(self, wx.CheckBox, value=CONFIG.get("CTRL_RenumberMorph", False), 516 name="CTRL_RenumberMorph", style=wx.NO_BORDER, 517 bind=((wx.EVT_CHECKBOX, self.onCheckBox),))) 518 519 gbm.addrow(PrepCtrl(self, wx.Button, label="Export Path", 520 style=wx.BORDER_NONE | wx.BU_EXACTFIT, size=btn_size, 521 bgcolor=STD_COLOR, fgcolor=FG_COLOR, 522 tooltip="Path to export current figure to.", 523 bind=((wx.EVT_LEFT_UP, self.onExportPath), 524 (wx.EVT_ENTER_WINDOW, self.onEnter_button), 525 (wx.EVT_LEAVE_WINDOW, self.onLeave_button)) 526 ), 527 PrepCtrl(self, wx.Button, label="Import Path", 528 style=wx.BORDER_NONE | wx.BU_EXACTFIT, size=btn_size, 529 bgcolor=STD_COLOR, fgcolor=FG_COLOR, 530 tooltip="Path where exported figures are stored.", 531 bind=((wx.EVT_LEFT_UP, self.onImportPath), 532 (wx.EVT_ENTER_WINDOW, self.onEnter_button), 533 (wx.EVT_LEAVE_WINDOW, self.onLeave_button)) 534 )) 535 536 gbm.addrow(LabelCtrl(self, label="Use Fileselection", 537 fgcolor=STD_COLOR, bgcolor=BG_COLOR), 538 PrepCtrl(self, wx.CheckBox, style=wx.NO_BORDER, 539 value=CONFIG.get("CTRL_SingleSelectFile", True), 540 name="CTRL_SingleSelectFile", 541 tooltip="Select file on each Import/Export", 542 bind=((wx.EVT_CHECKBOX, self.onCheckBox),))) 543 544 gbm.addrow(LabelCtrl(self, label="Export groups", 545 fgcolor=STD_COLOR, bgcolor=BG_COLOR), 546 PrepCtrl(self, wx.CheckBox, style=wx.NO_BORDER, 547 value=CONFIG.get("CTRL_ExportGroups", False), 548 name="CTRL_ExportGroups", 549 tooltip="Export actor groups", 550 bind=((wx.EVT_CHECKBOX, self.onCheckBox),))) 551 552 gbm.addrow(LabelCtrl(self, label="Export UV", 553 fgcolor=STD_COLOR, bgcolor=BG_COLOR), 554 PrepCtrl(self, wx.CheckBox, style=wx.NO_BORDER, 555 value=CONFIG.get("CTRL_ExportTexture", True), 556 name="CTRL_ExportTexture", 557 bind=((wx.EVT_CHECKBOX, self.onCheckBox),))) 558 559 gbm.addrow(LabelCtrl(self, label="LBL_Scale", 560 fgcolor=STD_COLOR, bgcolor=BG_COLOR), 561 ColoredCtrl(wx.TextCtrl(self, name="CTRL_Scale", 562 value=str(CONFIG.get("Scale", 1)), 563 style=wx.SIMPLE_BORDER | wx.TE_RIGHT), 564 fgcolor=FG_COLOR, bgcolor=BG_COLOR)) 565 566 gbm.sizer.AddGrowableCol(0) 567 gbm.sizer.AddGrowableCol(1) 568 569 sz = wx.BoxSizer(wx.VERTICAL) 570 sz.AddMany([ 571 (gbm.sizer, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, 10), 572 ]) 573 574 sz.SetSizeHints(self) 575 self.SetSizer(sz) 576 wx.CallAfter(self.updateUI) 577 578 def onSize(self, ev): 579 # CONFIG["Perspective"] = aui.SavePaneInfo(paneInfo) 580 # self._CONFIGCHANGED = True 581 ev.Skip() 582 583 def updateUI(self): 584 obj = self.FindWindowByName("LBL_FigureList") 585 if obj: 586 c = (0xff, 0, 0) if self.figurelist.GetCount() == 0 else STD_COLOR 587 obj.SetForegroundColour(c) 588 589 if self._CONFIGCHANGED: 590 write_config() 591 self._CONFIGCHANGED = False 592 593 594 if not STOPP_UPDATE_UI: 595 wx.CallLater(300, self.updateUI) 596 597 def onChoice(self, ev): 598 choice = ev.GetEventObject() 599 600 if choice.GetName() == "figurelist": # type: wx.Choice 601 idx = choice.GetCurrentSelection() 602 if idx == -1: 603 CONFIG["figure"] = "" 604 else: 605 CONFIG["figure"] = choice.GetString(idx) 606 607 def onRadioBox(self, ev): 608 try: 609 obj = ev.GetEventObject() 610 CONFIG[obj.GetName()] = obj.GetValue() 611 except Exception: 612 raise 613 finally: 614 ev.Skip() 615 616 def onCheckBox(self, ev): 617 try: 618 obj = ev.GetEventObject() 619 CONFIG[obj.GetName()] = obj.GetValue() 620 except Exception: 621 raise 622 finally: 623 ev.Skip() 624 625 def onExportModel(self, ev): 626 """:type ev: wx.Event""" 627 def reset_color(): 628 obj.SetBackgroundColour(STD_COLOR) 629 obj.Update() 630 631 try: 632 idx = self.figurelist.GetCurrentSelection() 633 if idx < 0: 634 ErrDialog("No figure selected.") 635 return 636 637 sc = wx.FindWindowByName("CTRL_Scale") 638 CONFIG["Scale"] = sc.GetValue() 639 640 obj = wx.FindWindowByName("CTRL_MorphName") 641 CONFIG["CTRL_MorphName"] = obj.GetValue() 642 obj = ev.GetEventObject() 643 obj.SetBackgroundColour((0xff, 0, 0)) 644 obj.Update() 645 646 figurename = self.figurelist.GetString(idx) 647 figure = poser.Scene().Figure(figurename.strip()) 648 do_export(figure, onFinish=None) 649 obj.SetBackgroundColour(STD_COLOR) 650 obj.Update() 651 652 except Exception: 653 raise 654 finally: 655 ev.Skip() 656 657 def onImportModel(self, ev): 658 try: 659 sc = wx.FindWindowByName("CTRL_Scale") 660 CONFIG["Scale"] = sc.GetValue() 661 idx = self.figurelist.GetCurrentSelection() 662 if idx < 0: 663 ErrDialog("No figure selected.") 664 return 665 666 figurename = self.figurelist.GetString(idx) 667 figure = poser.Scene().Figure(figurename.strip()) 668 morph_ctrl = wx.FindWindowByName("CTRL_MorphName") 669 morphname = morph_ctrl.GetValue().strip() if morph_ctrl else "MORPH" 670 671 obj = ev.GetEventObject() 672 obj.SetBackgroundColour((0xff, 0, 0)) 673 obj.Update() 674 do_import(figure, morphname) 675 obj.SetBackgroundColour(STD_COLOR) 676 677 check = wx.FindWindowByName("CTRL_RenumberMorph") 678 if check and check.GetValue(): 679 morphname, _, nr = morphname.partition(" ") 680 morphname += " " + str(int(nr or 0) + 1) 681 morph_ctrl.SetValue(morphname) 682 except Exception: 683 raise 684 finally: 685 ev.Skip() 686 687 def onExportPath(self, ev): 688 try: 689 defaultpath = CONFIG.get("ExportPath", 690 CONFIG.get("ImportPath", 691 CONFIG.get("BASEPATH", BASEPATH))) 692 with wx.DirDialog(self, 693 "Choose Export Directory", defaultpath, 694 style=wx.DD_DEFAULT_STYLE 695 ) as dlg: 696 if dlg.ShowModal() == wx.ID_OK: 697 CONFIG["ExportPath"] = dlg.GetPath() 698 self._CONFIGCHANGED = True 699 700 except Exception: 701 raise 702 703 def onImportPath(self, ev): 704 try: 705 defaultpath = CONFIG.get("ImportPath", 706 CONFIG.get("ExportPath", 707 CONFIG.get("BASEPATH", BASEPATH))) 708 with wx.DirDialog(None, 709 "Choose Import Directory", defaultpath, 710 style=wx.DD_DEFAULT_STYLE 711 ) as dlg: 712 if dlg.ShowModal() == wx.ID_OK: 713 CONFIG["ImportPath"] = dlg.GetPath() 714 except Exception: 715 raise 716 finally: 717 ev.Skip() 718 719 def onEnter_button(self, ev): 720 obj = ev.GetEventObject() 721 if obj.BackgroundColour == STD_COLOR: 722 obj.SetBackgroundColour(HL_COLOR) 723 ev.Skip() 724 725 def onLeave_button(self, event): 726 obj = event.GetEventObject() 727 if obj.BackgroundColour == HL_COLOR: 728 obj.SetBackgroundColour(STD_COLOR) 729 event.Skip() 730 731 def onClose(self, event): 732 global STOPP_UPDATE_UI 733 STOPP_UPDATE_UI = True 734 735 CONFIG["Perspective"] = aui.SavePaneInfo(paneInfo) 736 CONFIG["CTRL_MorphName"] = wx.FindWindowByName("CTRL_MorphName").GetValue() 737 CONFIG["Scale"] = wx.FindWindowByName("CTRL_Scale").GetValue() 738 739 write_config() 740 741 def updateFigurelist(self, *ev): 742 if poser: 743 while self.figurelist.GetCount(): 744 self.figurelist.Delete(0) 745 746 for idx, fig in enumerate(poser.Scene().Figures()): 747 self.figurelist.Append(fig.Name()) 748 if fig == poser.Scene().CurrentFigure(): 749 self.figurelist.SetSelection(idx) 750 751 752 if __name__ == "__main__": 753 read_config() 754 aui = poser.WxAuiManager() 755 root = aui.GetManagedWindow() 756 SYS_FONT = root.GetFont() 757 758 paneInfo = aui.GetPane(APP_NAME) 759 if paneInfo.IsOk(): 760 aui.ClosePane(paneInfo) 761 762 paneInfo = wx.aui.AuiPaneInfo() 763 savedinfo = CONFIG.get("Perspective", None) 764 if savedinfo is not None: 765 aui.LoadPaneInfo(savedinfo, paneInfo) 766 else: 767 paneInfo.name = APP_NAME 768 paneInfo.Resizable(True).FloatingSize(wx.Size(250, 280)).BestSize(wx.Size(200, 230)) 769 paneInfo.Movable(True).DestroyOnClose(True) 770 771 paneInfo.Name(APP_NAME).Caption(" Poser Morphed ExIm +++ 0.98a ") 772 773 MainPanel = AppPanel(parent=root, name=APP_NAME) 774 aui.AddPane(MainPanel, paneInfo) 775 paneInfo.Show() 776 aui.Update() 777