FullbodyMorphs_wx3.py
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