Ejemplo n.º 1
0
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int):
        super().__init__(frame, parent, tab_idx)
        self.convert_csv_worker = None

        self.description_txt = wx.StaticText(
            self, wx.ID_ANY,
            "指定されたVMDファイルの解析結果を、ボーン/モーフ/カメラに分けてCSVファイルとして出力します。",
            wx.DefaultPosition, wx.DefaultSize, 0)
        self.sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                         wx.DefaultSize, wx.LI_HORIZONTAL)
        self.sizer.Add(self.static_line, 0, wx.EXPAND | wx.ALL, 5)

        # CSVファイルコントロール
        self.vmd_file_ctrl = BaseFilePickerCtrl(frame, self, u"VMDファイル", u"VMDファイルを開く", ("vmd"), wx.FLP_DEFAULT_STYLE, \
                                                u"CSVに変換したいVMDのファイルパスを指定してください。", \
                                                is_aster=False, is_save=False, set_no=0)
        self.sizer.Add(self.vmd_file_ctrl.sizer, 0, wx.EXPAND | wx.ALL, 0)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # CSV変換実行ボタン
        self.csv_btn_ctrl = wx.Button(self, wx.ID_ANY, u"CSV変換実行",
                                      wx.DefaultPosition, wx.Size(200, 50), 0)
        self.csv_btn_ctrl.SetToolTip(u"VMDをCSVに変換します。")
        self.csv_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_convert_csv)
        btn_sizer.Add(self.csv_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.fit()

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_CSV_THREAD, self.on_convert_csv_result)
Ejemplo n.º 2
0
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int,
                 file_hitories: dict):
        super().__init__(frame, parent, tab_idx)
        self.file_hitories = file_hitories
        self.timer = None

        # ファイルセット
        self.file_set = SizingFileSet(frame, self, self.file_hitories, 1)
        self.sizer.Add(self.file_set.set_sizer, 0, wx.ALL, 0)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 変換前チェックボタン
        self.check_btn_ctrl = wx.Button(self, wx.ID_ANY,
                                        u"変換前チェック", wx.DefaultPosition,
                                        wx.Size(200, 50), 0)
        self.check_btn_ctrl.SetToolTip(u"入力されたファイル情報で処理可能かどうか、チェックを行います。")
        self.check_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        self.check_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_check_click)
        btn_sizer.Add(self.check_btn_ctrl, 0, wx.ALL, 5)

        # 実行ボタン
        self.exec_btn_ctrl = wx.Button(self, wx.ID_ANY, u"VMDサイジング実行",
                                       wx.DefaultPosition, wx.Size(200, 50), 0)
        self.exec_btn_ctrl.SetToolTip(u"VMDサイジング処理を実行します。")
        self.exec_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        self.exec_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_exec_click)
        btn_sizer.Add(self.exec_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, -1), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.fit()
Ejemplo n.º 3
0
    def __init__(self, frame: wx.Frame, arm_ik2fk: wx.Notebook, tab_idx: int):
        super().__init__(frame, arm_ik2fk, tab_idx)
        self.convert_arm_ik2fk_worker = None

        self.header_sizer = wx.BoxSizer(wx.VERTICAL)

        self.description_txt = wx.StaticText(self, wx.ID_ANY, u"腕IKを腕FK(腕・ひじ・手首)に変換します" \
                                             + "\n不要キー削除を行うと、キーが間引きされます。キー間がオリジナルから多少ずれ、またそれなりに時間がかかります。", wx.DefaultPosition, wx.DefaultSize, 0)
        self.header_sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                           wx.DefaultSize, wx.LI_HORIZONTAL)
        self.header_sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5)

        # 対象VMDファイルコントロール
        self.arm_ik2fk_vmd_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"対象モーションVMD/VPD", u"対象モーションVMDファイルを開く", ("vmd", "vpd"), wx.FLP_DEFAULT_STYLE, \
                                                             u"調整したい対象モーションのVMDパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                             file_model_spacer=46, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="arm_ik2fk_vmd", is_change_output=True, \
                                                             is_aster=False, is_save=False, set_no=1)
        self.header_sizer.Add(self.arm_ik2fk_vmd_file_ctrl.sizer, 1, wx.EXPAND,
                              0)

        # 対象PMXファイルコントロール
        self.arm_ik2fk_model_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"適用モデルPMX", u"適用モデルPMXファイルを開く", ("pmx"), wx.FLP_DEFAULT_STYLE, \
                                                               u"モーションを適用したいモデルのPMXパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                               file_model_spacer=60, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="arm_ik2fk_pmx", \
                                                               is_change_output=True, is_aster=False, is_save=False, set_no=1)
        self.header_sizer.Add(self.arm_ik2fk_model_file_ctrl.sizer, 1,
                              wx.EXPAND, 0)

        # 出力先VMDファイルコントロール
        self.output_arm_ik2fk_vmd_file_ctrl = BaseFilePickerCtrl(frame, self, u"出力対象VMD", u"出力対象VMDファイルを開く", ("vmd"), wx.FLP_OVERWRITE_PROMPT | wx.FLP_SAVE | wx.FLP_USE_TEXTCTRL, \
                                                                 u"調整結果の対象VMD出力パスを指定してください。\n対象VMDファイル名に基づいて自動生成されますが、任意のパスに変更することも可能です。", \
                                                                 is_aster=False, is_save=True, set_no=1)
        self.header_sizer.Add(self.output_arm_ik2fk_vmd_file_ctrl.sizer, 1,
                              wx.EXPAND, 0)

        # 不要キー削除処理
        self.remove_unnecessary_flg_ctrl = wx.CheckBox(self, wx.ID_ANY,
                                                       u"不要キー削除処理を追加実行する",
                                                       wx.DefaultPosition,
                                                       wx.DefaultSize, 0)
        self.remove_unnecessary_flg_ctrl.SetToolTip(
            u"チェックを入れると、不要キー削除処理を追加で実行します。キーが減る分、キー間が少しズレる事があります。")
        self.header_sizer.Add(self.remove_unnecessary_flg_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(self.header_sizer, 0, wx.EXPAND | wx.ALL, 5)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 多段分割変換実行ボタン
        self.arm_ik2fk_btn_ctrl = wx.Button(self, wx.ID_ANY,
                                            u"腕IK変換", wx.DefaultPosition,
                                            wx.Size(200, 50), 0)
        self.arm_ik2fk_btn_ctrl.SetToolTip(u"足FKを足IKに変換したモーションを再生成します。")
        self.arm_ik2fk_btn_ctrl.Bind(wx.EVT_LEFT_DOWN,
                                     self.on_convert_arm_ik2fk)
        self.arm_ik2fk_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        btn_sizer.Add(self.arm_ik2fk_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.Layout()
        self.fit()

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_SMOOTH_THREAD, self.on_convert_arm_ik2fk_result)
Ejemplo n.º 4
0
class ArmIKtoFKPanel(BasePanel):
    def __init__(self, frame: wx.Frame, arm_ik2fk: wx.Notebook, tab_idx: int):
        super().__init__(frame, arm_ik2fk, tab_idx)
        self.convert_arm_ik2fk_worker = None

        self.header_sizer = wx.BoxSizer(wx.VERTICAL)

        self.description_txt = wx.StaticText(self, wx.ID_ANY, u"腕IKを腕FK(腕・ひじ・手首)に変換します" \
                                             + "\n不要キー削除を行うと、キーが間引きされます。キー間がオリジナルから多少ずれ、またそれなりに時間がかかります。", wx.DefaultPosition, wx.DefaultSize, 0)
        self.header_sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                           wx.DefaultSize, wx.LI_HORIZONTAL)
        self.header_sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5)

        # 対象VMDファイルコントロール
        self.arm_ik2fk_vmd_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"対象モーションVMD/VPD", u"対象モーションVMDファイルを開く", ("vmd", "vpd"), wx.FLP_DEFAULT_STYLE, \
                                                             u"調整したい対象モーションのVMDパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                             file_model_spacer=46, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="arm_ik2fk_vmd", is_change_output=True, \
                                                             is_aster=False, is_save=False, set_no=1)
        self.header_sizer.Add(self.arm_ik2fk_vmd_file_ctrl.sizer, 1, wx.EXPAND,
                              0)

        # 対象PMXファイルコントロール
        self.arm_ik2fk_model_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"適用モデルPMX", u"適用モデルPMXファイルを開く", ("pmx"), wx.FLP_DEFAULT_STYLE, \
                                                               u"モーションを適用したいモデルのPMXパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                               file_model_spacer=60, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="arm_ik2fk_pmx", \
                                                               is_change_output=True, is_aster=False, is_save=False, set_no=1)
        self.header_sizer.Add(self.arm_ik2fk_model_file_ctrl.sizer, 1,
                              wx.EXPAND, 0)

        # 出力先VMDファイルコントロール
        self.output_arm_ik2fk_vmd_file_ctrl = BaseFilePickerCtrl(frame, self, u"出力対象VMD", u"出力対象VMDファイルを開く", ("vmd"), wx.FLP_OVERWRITE_PROMPT | wx.FLP_SAVE | wx.FLP_USE_TEXTCTRL, \
                                                                 u"調整結果の対象VMD出力パスを指定してください。\n対象VMDファイル名に基づいて自動生成されますが、任意のパスに変更することも可能です。", \
                                                                 is_aster=False, is_save=True, set_no=1)
        self.header_sizer.Add(self.output_arm_ik2fk_vmd_file_ctrl.sizer, 1,
                              wx.EXPAND, 0)

        # 不要キー削除処理
        self.remove_unnecessary_flg_ctrl = wx.CheckBox(self, wx.ID_ANY,
                                                       u"不要キー削除処理を追加実行する",
                                                       wx.DefaultPosition,
                                                       wx.DefaultSize, 0)
        self.remove_unnecessary_flg_ctrl.SetToolTip(
            u"チェックを入れると、不要キー削除処理を追加で実行します。キーが減る分、キー間が少しズレる事があります。")
        self.header_sizer.Add(self.remove_unnecessary_flg_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(self.header_sizer, 0, wx.EXPAND | wx.ALL, 5)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 多段分割変換実行ボタン
        self.arm_ik2fk_btn_ctrl = wx.Button(self, wx.ID_ANY,
                                            u"腕IK変換", wx.DefaultPosition,
                                            wx.Size(200, 50), 0)
        self.arm_ik2fk_btn_ctrl.SetToolTip(u"足FKを足IKに変換したモーションを再生成します。")
        self.arm_ik2fk_btn_ctrl.Bind(wx.EVT_LEFT_DOWN,
                                     self.on_convert_arm_ik2fk)
        self.arm_ik2fk_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        btn_sizer.Add(self.arm_ik2fk_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.Layout()
        self.fit()

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_SMOOTH_THREAD, self.on_convert_arm_ik2fk_result)

    def on_wheel_spin_ctrl(self, event: wx.Event, inc=1):
        self.frame.on_wheel_spin_ctrl(event, inc)
        self.set_output_vmd_path(event)

    # ファイル変更時の処理
    def on_change_file(self, event: wx.Event):
        self.set_output_vmd_path(event, is_force=True)

    def set_output_vmd_path(self, event, is_force=False):
        output_arm_ik2fk_vmd_path = MFileUtils.get_output_arm_ik2fk_vmd_path(
            self.arm_ik2fk_vmd_file_ctrl.file_ctrl.GetPath(),
            self.arm_ik2fk_model_file_ctrl.file_ctrl.GetPath(),
            self.output_arm_ik2fk_vmd_file_ctrl.file_ctrl.GetPath(), is_force)

        self.output_arm_ik2fk_vmd_file_ctrl.file_ctrl.SetPath(
            output_arm_ik2fk_vmd_path)

        if len(output_arm_ik2fk_vmd_path) >= 255 and os.name == "nt":
            logger.error("生成予定のファイルパスがWindowsの制限を超えています。\n生成予定パス: {0}".format(
                output_arm_ik2fk_vmd_path),
                         decoration=MLogger.DECORATION_BOX)

    # フォーム無効化
    def disable(self):
        self.arm_ik2fk_vmd_file_ctrl.disable()
        self.arm_ik2fk_model_file_ctrl.disable()
        self.output_arm_ik2fk_vmd_file_ctrl.disable()
        self.arm_ik2fk_btn_ctrl.Disable()

    # フォーム無効化
    def enable(self):
        self.arm_ik2fk_vmd_file_ctrl.enable()
        self.arm_ik2fk_model_file_ctrl.enable()
        self.output_arm_ik2fk_vmd_file_ctrl.enable()
        self.arm_ik2fk_btn_ctrl.Enable()

    def on_doubleclick(self, event: wx.Event):
        self.timer.Stop()
        logger.warning("ダブルクリックされました。", decoration=MLogger.DECORATION_BOX)
        event.Skip(False)
        return False

    # 多段分割変換
    def on_convert_arm_ik2fk(self, event: wx.Event):
        self.timer = wx.Timer(self, TIMER_ID)
        self.timer.Start(200)
        self.Bind(wx.EVT_TIMER, self.on_convert, id=TIMER_ID)

    # 多段分割変換
    def on_convert(self, event: wx.Event):
        self.timer.Stop()
        self.Unbind(wx.EVT_TIMER, id=TIMER_ID)
        # フォーム無効化
        self.disable()
        # タブ固定
        self.fix_tab()
        # コンソールクリア
        self.console_ctrl.Clear()
        # 出力先を多段分割パネルのコンソールに変更
        sys.stdout = self.console_ctrl

        self.arm_ik2fk_vmd_file_ctrl.save()
        self.arm_ik2fk_model_file_ctrl.save()

        # JSON出力
        MFileUtils.save_history(self.frame.mydir_path,
                                self.frame.file_hitories)

        self.elapsed_time = 0
        result = True
        result = self.arm_ik2fk_vmd_file_ctrl.is_valid(
        ) and self.arm_ik2fk_model_file_ctrl.is_valid() and result

        if not result:
            # 終了音
            self.frame.sound_finish()
            # タブ移動可
            self.release_tab()
            # フォーム有効化
            self.enable()

            return result

        # 腕IK変換変換開始
        if self.arm_ik2fk_btn_ctrl.GetLabel(
        ) == "腕IK変換停止" and self.convert_arm_ik2fk_worker:
            # フォーム無効化
            self.disable()
            # 停止状態でボタン押下時、停止
            self.convert_arm_ik2fk_worker.stop()

            # タブ移動可
            self.frame.release_tab()
            # フォーム有効化
            self.frame.enable()
            # ワーカー終了
            self.convert_arm_ik2fk_worker = None
            # プログレス非表示
            self.gauge_ctrl.SetValue(0)

            logger.warning("腕IK変換を中断します。", decoration=MLogger.DECORATION_BOX)
            self.arm_ik2fk_btn_ctrl.SetLabel("腕IK変換")

            event.Skip(False)
        elif not self.convert_arm_ik2fk_worker:
            # フォーム無効化
            self.disable()
            # タブ固定
            self.fix_tab()
            # コンソールクリア
            self.console_ctrl.Clear()
            # ラベル変更
            self.arm_ik2fk_btn_ctrl.SetLabel("腕IK変換停止")
            self.arm_ik2fk_btn_ctrl.Enable()

            self.convert_arm_ik2fk_worker = ArmIKtoFKWorkerThread(
                self.frame, ArmIKtoFKThreadEvent, self.frame.is_saving,
                self.frame.is_out_log)
            self.convert_arm_ik2fk_worker.start()

            event.Skip()
        else:
            logger.error("まだ処理が実行中です。終了してから再度実行してください。",
                         decoration=MLogger.DECORATION_BOX)
            event.Skip(False)

        return result

    # 多段分割変換完了処理
    def on_convert_arm_ik2fk_result(self, event: wx.Event):
        self.elapsed_time = event.elapsed_time
        logger.info("\n処理時間: %s", self.show_worked_time())
        self.arm_ik2fk_btn_ctrl.SetLabel("腕IK変換")

        # 終了音
        self.frame.sound_finish()

        # タブ移動可
        self.release_tab()
        # フォーム有効化
        self.enable()
        # ワーカー終了
        self.convert_arm_ik2fk_worker = None
        # プログレス非表示
        self.gauge_ctrl.SetValue(0)

    def show_worked_time(self):
        # 経過秒数を時分秒に変換
        td_m, td_s = divmod(self.elapsed_time, 60)

        if td_m == 0:
            worked_time = "{0:02d}秒".format(int(td_s))
        else:
            worked_time = "{0:02d}分{1:02d}秒".format(int(td_m), int(td_s))

        return worked_time
Ejemplo n.º 5
0
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int):
        super().__init__(frame, parent, tab_idx)
        self.convert_smooth_worker = None

        self.header_sizer = wx.BoxSizer(wx.VERTICAL)

        self.description_txt = wx.StaticText(self, wx.ID_ANY, u"指定されたVMDファイルに対して、キーを分割し、滑らかな補間曲線で繋いで、再出力します。\n" \
                                             + "スムージング回数1回で、全打ちとなり、2回目以降はフィルタリングした後に補間曲線で繋ぎます。", wx.DefaultPosition, wx.DefaultSize, 0)
        self.header_sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                           wx.DefaultSize, wx.LI_HORIZONTAL)
        self.header_sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5)

        # 対象VMDファイルコントロール
        self.smooth_vmd_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"対象モーションVMD", u"対象モーションVMDファイルを開く", ("vmd"), wx.FLP_DEFAULT_STYLE, \
                                                          u"調整したい対象モーションのVMDパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                          file_model_spacer=0, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="smooth_vmd", is_change_output=True, \
                                                          is_aster=False, is_save=False, set_no=1)
        self.header_sizer.Add(self.smooth_vmd_file_ctrl.sizer, 1, wx.EXPAND, 0)

        # 対象PMXファイルコントロール
        self.smooth_model_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"適用モデルPMX", u"適用モデルPMXファイルを開く", ("pmx"), wx.FLP_DEFAULT_STYLE, \
                                                            u"モーションを適用したいモデルのPMXパスを指定してください。\n人体モデル以外にも適用可能です。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                            file_model_spacer=0, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="smooth_pmx", \
                                                            is_change_output=True, is_aster=False, is_save=False, set_no=1)
        self.header_sizer.Add(self.smooth_model_file_ctrl.sizer, 1, wx.EXPAND,
                              0)

        # 出力先VMDファイルコントロール
        self.output_smooth_vmd_file_ctrl = BaseFilePickerCtrl(frame, self, u"出力対象VMD", u"出力対象VMDファイルを開く", ("vmd"), wx.FLP_OVERWRITE_PROMPT | wx.FLP_SAVE | wx.FLP_USE_TEXTCTRL, \
                                                              u"調整結果の対象VMD出力パスを指定してください。\n対象VMDファイル名に基づいて自動生成されますが、任意のパスに変更することも可能です。", \
                                                              is_aster=False, is_save=True, set_no=1)
        self.header_sizer.Add(self.output_smooth_vmd_file_ctrl.sizer, 1,
                              wx.EXPAND, 0)

        self.sizer.Add(self.header_sizer, 0, wx.EXPAND | wx.ALL, 5)

        self.setting_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 処理回数
        self.loop_cnt_txt = wx.StaticText(self, wx.ID_ANY, u"処理回数",
                                          wx.DefaultPosition, wx.DefaultSize,
                                          0)
        self.setting_sizer.Add(self.loop_cnt_txt, 0, wx.ALL, 5)

        self.loop_cnt_ctrl = wx.SpinCtrl(self,
                                         id=wx.ID_ANY,
                                         size=wx.Size(60, -1),
                                         value="2",
                                         min=1,
                                         max=99999999,
                                         initial=2)
        self.loop_cnt_ctrl.SetToolTip(
            u"スムージングを行う回数を指定してください。\n1回だと全打ちになります。\n2回目以降はフィルタをかけた上で間引きします。\n回数が増えると、変化が遅く、弱くなります。"
        )
        self.loop_cnt_ctrl.Bind(wx.EVT_SPINCTRL, self.on_change_file)
        self.setting_sizer.Add(self.loop_cnt_ctrl, 0, wx.ALL, 5)

        # 補間
        self.interpolation_txt = wx.StaticText(self, wx.ID_ANY, u"補間方法",
                                               wx.DefaultPosition,
                                               wx.DefaultSize, 0)
        self.setting_sizer.Add(self.interpolation_txt, 0, wx.ALL, 5)

        self.interpolation_ctrl = wx.Choice(
            self,
            id=wx.ID_ANY,
            choices=["補間曲線に従う", "補間曲線無視(円形)", "補間曲線無視(曲線)"])
        self.interpolation_ctrl.SetSelection(0)
        self.interpolation_ctrl.SetToolTip(u"キーとキーの補間方法を指定してください。\n「補間曲線に従う」は、補間曲線に従って繋ぎます。" \
                                           + "\n「補間曲線無視(円形)」は、補間曲線を無視して、\n3つのキーを円周上に置いた円になるように補間します。" \
                                           + "\n「補間曲線無視(曲線)」は、補間曲線を無視して、\nキーを滑らかな曲線(カトマル曲線)で繋いで補間します。")
        self.interpolation_ctrl.Bind(wx.EVT_CHOICE, self.on_change_file)
        self.setting_sizer.Add(self.interpolation_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(self.setting_sizer, 0, wx.EXPAND | wx.ALL, 5)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # スムージング変換実行ボタン
        self.smooth_btn_ctrl = wx.Button(self, wx.ID_ANY,
                                         u"スムージング実行", wx.DefaultPosition,
                                         wx.Size(200, 50), 0)
        self.smooth_btn_ctrl.SetToolTip(u"VMDを滑らかに繋いだモーションを再生成します。")
        self.smooth_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_convert_smooth)
        btn_sizer.Add(self.smooth_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.Layout()
        self.fit()

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_SMOOTH_THREAD, self.on_convert_smooth_result)
Ejemplo n.º 6
0
class SmoothPanel(BasePanel):
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int):
        super().__init__(frame, parent, tab_idx)
        self.convert_smooth_worker = None

        self.header_sizer = wx.BoxSizer(wx.VERTICAL)

        self.description_txt = wx.StaticText(self, wx.ID_ANY, u"指定されたVMDファイルに対して、キーを分割し、滑らかな補間曲線で繋いで、再出力します。\n" \
                                             + "スムージング回数1回で、全打ちとなり、2回目以降はフィルタリングした後に補間曲線で繋ぎます。", wx.DefaultPosition, wx.DefaultSize, 0)
        self.header_sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                           wx.DefaultSize, wx.LI_HORIZONTAL)
        self.header_sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5)

        # 対象VMDファイルコントロール
        self.smooth_vmd_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"対象モーションVMD", u"対象モーションVMDファイルを開く", ("vmd"), wx.FLP_DEFAULT_STYLE, \
                                                          u"調整したい対象モーションのVMDパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                          file_model_spacer=0, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="smooth_vmd", is_change_output=True, \
                                                          is_aster=False, is_save=False, set_no=1)
        self.header_sizer.Add(self.smooth_vmd_file_ctrl.sizer, 1, wx.EXPAND, 0)

        # 対象PMXファイルコントロール
        self.smooth_model_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"適用モデルPMX", u"適用モデルPMXファイルを開く", ("pmx"), wx.FLP_DEFAULT_STYLE, \
                                                            u"モーションを適用したいモデルのPMXパスを指定してください。\n人体モデル以外にも適用可能です。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                            file_model_spacer=0, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="smooth_pmx", \
                                                            is_change_output=True, is_aster=False, is_save=False, set_no=1)
        self.header_sizer.Add(self.smooth_model_file_ctrl.sizer, 1, wx.EXPAND,
                              0)

        # 出力先VMDファイルコントロール
        self.output_smooth_vmd_file_ctrl = BaseFilePickerCtrl(frame, self, u"出力対象VMD", u"出力対象VMDファイルを開く", ("vmd"), wx.FLP_OVERWRITE_PROMPT | wx.FLP_SAVE | wx.FLP_USE_TEXTCTRL, \
                                                              u"調整結果の対象VMD出力パスを指定してください。\n対象VMDファイル名に基づいて自動生成されますが、任意のパスに変更することも可能です。", \
                                                              is_aster=False, is_save=True, set_no=1)
        self.header_sizer.Add(self.output_smooth_vmd_file_ctrl.sizer, 1,
                              wx.EXPAND, 0)

        self.sizer.Add(self.header_sizer, 0, wx.EXPAND | wx.ALL, 5)

        self.setting_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 処理回数
        self.loop_cnt_txt = wx.StaticText(self, wx.ID_ANY, u"処理回数",
                                          wx.DefaultPosition, wx.DefaultSize,
                                          0)
        self.setting_sizer.Add(self.loop_cnt_txt, 0, wx.ALL, 5)

        self.loop_cnt_ctrl = wx.SpinCtrl(self,
                                         id=wx.ID_ANY,
                                         size=wx.Size(60, -1),
                                         value="2",
                                         min=1,
                                         max=99999999,
                                         initial=2)
        self.loop_cnt_ctrl.SetToolTip(
            u"スムージングを行う回数を指定してください。\n1回だと全打ちになります。\n2回目以降はフィルタをかけた上で間引きします。\n回数が増えると、変化が遅く、弱くなります。"
        )
        self.loop_cnt_ctrl.Bind(wx.EVT_SPINCTRL, self.on_change_file)
        self.setting_sizer.Add(self.loop_cnt_ctrl, 0, wx.ALL, 5)

        # 補間
        self.interpolation_txt = wx.StaticText(self, wx.ID_ANY, u"補間方法",
                                               wx.DefaultPosition,
                                               wx.DefaultSize, 0)
        self.setting_sizer.Add(self.interpolation_txt, 0, wx.ALL, 5)

        self.interpolation_ctrl = wx.Choice(
            self,
            id=wx.ID_ANY,
            choices=["補間曲線に従う", "補間曲線無視(円形)", "補間曲線無視(曲線)"])
        self.interpolation_ctrl.SetSelection(0)
        self.interpolation_ctrl.SetToolTip(u"キーとキーの補間方法を指定してください。\n「補間曲線に従う」は、補間曲線に従って繋ぎます。" \
                                           + "\n「補間曲線無視(円形)」は、補間曲線を無視して、\n3つのキーを円周上に置いた円になるように補間します。" \
                                           + "\n「補間曲線無視(曲線)」は、補間曲線を無視して、\nキーを滑らかな曲線(カトマル曲線)で繋いで補間します。")
        self.interpolation_ctrl.Bind(wx.EVT_CHOICE, self.on_change_file)
        self.setting_sizer.Add(self.interpolation_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(self.setting_sizer, 0, wx.EXPAND | wx.ALL, 5)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # スムージング変換実行ボタン
        self.smooth_btn_ctrl = wx.Button(self, wx.ID_ANY,
                                         u"スムージング実行", wx.DefaultPosition,
                                         wx.Size(200, 50), 0)
        self.smooth_btn_ctrl.SetToolTip(u"VMDを滑らかに繋いだモーションを再生成します。")
        self.smooth_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_convert_smooth)
        btn_sizer.Add(self.smooth_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.Layout()
        self.fit()

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_SMOOTH_THREAD, self.on_convert_smooth_result)

    # ファイル変更時の処理
    def on_change_file(self, event: wx.Event):
        self.set_output_vmd_path(event)

    def set_output_vmd_path(self, event, is_force=False):
        output_smooth_vmd_path = MFileUtils.get_output_smooth_vmd_path(
            self.smooth_vmd_file_ctrl.file_ctrl.GetPath(),
            self.smooth_model_file_ctrl.file_ctrl.GetPath(),
            self.output_smooth_vmd_file_ctrl.file_ctrl.GetPath(),
            self.interpolation_ctrl.GetSelection(),
            self.loop_cnt_ctrl.GetValue(), is_force)

        self.output_smooth_vmd_file_ctrl.file_ctrl.SetPath(
            output_smooth_vmd_path)

        if len(output_smooth_vmd_path) >= 255 and os.name == "nt":
            logger.error("生成予定のファイルパスがWindowsの制限を超えています。\n生成予定パス: {0}".format(
                output_smooth_vmd_path),
                         decoration=MLogger.DECORATION_BOX)

    # フォーム無効化
    def disable(self):
        self.smooth_vmd_file_ctrl.disable()
        self.smooth_model_file_ctrl.disable()
        self.output_smooth_vmd_file_ctrl.disable()
        self.loop_cnt_ctrl.Disable()
        self.interpolation_ctrl.Disable()
        self.smooth_btn_ctrl.Disable()

    # フォーム無効化
    def enable(self):
        self.smooth_vmd_file_ctrl.enable()
        self.smooth_model_file_ctrl.enable()
        self.output_smooth_vmd_file_ctrl.enable()
        self.loop_cnt_ctrl.Enable()
        self.interpolation_ctrl.Enable()
        self.smooth_btn_ctrl.Enable()

    # スムージング変換
    def on_convert_smooth(self, event: wx.Event):
        # フォーム無効化
        self.disable()
        # タブ固定
        self.fix_tab()
        # コンソールクリア
        self.console_ctrl.Clear()
        # 出力先をスムージングパネルのコンソールに変更
        sys.stdout = self.console_ctrl

        wx.GetApp().Yield()

        self.smooth_vmd_file_ctrl.save()
        self.smooth_model_file_ctrl.save()

        # JSON出力
        MFileUtils.save_history(self.frame.mydir_path,
                                self.frame.file_hitories)

        self.elapsed_time = 0
        result = True
        result = self.smooth_vmd_file_ctrl.is_valid(
        ) and self.smooth_model_file_ctrl.is_valid() and result

        if not result:
            # 終了音
            self.frame.sound_finish()
            # タブ移動可
            self.release_tab()
            # フォーム有効化
            self.enable()
            # 出力先をデフォルトに戻す
            sys.stdout = self.frame.file_panel_ctrl.console_ctrl

            return result

        # スムージング変換開始
        if self.convert_smooth_worker:
            logger.error("まだ処理が実行中です。終了してから再度実行してください。",
                         decoration=MLogger.DECORATION_BOX)
        else:
            # 別スレッドで実行
            self.convert_smooth_worker = SmoothWorkerThread(
                self.frame, SmoothThreadEvent, self.frame.is_saving)
            self.convert_smooth_worker.start()

        return result

        event.Skip()

    # スムージング変換完了処理
    def on_convert_smooth_result(self, event: wx.Event):
        self.elapsed_time = event.elapsed_time
        logger.info("\n処理時間: %s", self.show_worked_time())

        # 終了音
        self.frame.sound_finish()

        # タブ移動可
        self.release_tab()
        # フォーム有効化
        self.enable()
        # ワーカー終了
        self.convert_smooth_worker = None
        # プログレス非表示
        self.gauge_ctrl.SetValue(0)

        # 出力先をデフォルトに戻す
        sys.stdout = self.frame.file_panel_ctrl.console_ctrl

    def show_worked_time(self):
        # 経過秒数を時分秒に変換
        td_m, td_s = divmod(self.elapsed_time, 60)

        if td_m == 0:
            worked_time = "{0:02d}秒".format(int(td_s))
        else:
            worked_time = "{0:02d}分{1:02d}秒".format(int(td_m), int(td_s))

        return worked_time
Ejemplo n.º 7
0
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int,
                 file_hitories: dict):
        super().__init__(frame, parent, tab_idx)
        self.file_hitories = file_hitories
        self.timer = None
        self.tree_process_dict = {}

        # ファイルセット
        self.file_set = SizingFileSet(frame, self, self.file_hitories, 1)
        self.sizer.Add(self.file_set.set_sizer, 0, wx.ALL, 0)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 変換前チェックボタン
        self.check_btn_ctrl = wx.Button(self, wx.ID_ANY,
                                        u"変換前チェック", wx.DefaultPosition,
                                        wx.Size(200, 50), 0)
        self.check_btn_ctrl.SetToolTip(u"入力されたファイル情報で処理可能かどうか、チェックを行います。")
        self.check_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        self.check_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_check_click)
        btn_sizer.Add(self.check_btn_ctrl, 0, wx.ALL, 5)

        # 実行ボタン
        self.exec_btn_ctrl = wx.Button(self, wx.ID_ANY, u"VMDサイジング実行",
                                       wx.DefaultPosition, wx.Size(200, 50), 0)
        self.exec_btn_ctrl.SetToolTip(u"VMDサイジング処理を実行します。")
        self.exec_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        self.exec_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_exec_click)
        btn_sizer.Add(self.exec_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, -1), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        status_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 進捗ダイアログ
        self.process_dialog = None

        # 進捗ステータス
        self.before_bracket_ctrl = wx.TextCtrl(
            self, wx.ID_ANY, "(", wx.DefaultPosition, wx.Size(5, -1),
            wx.TE_READONLY | wx.BORDER_NONE | wx.WANTS_CHARS)
        self.before_bracket_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        status_sizer.Add(self.before_bracket_ctrl, 0, wx.ALIGN_LEFT, 5)

        self.now_process_ctrl = StatusCtrl(
            self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition,
            wx.Size(20, -1), wx.TE_READONLY | wx.BORDER_NONE | wx.WANTS_CHARS)
        self.now_process_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.now_process_ctrl.SetToolTip(
            u"現在進んでいるの大まかな処理数です。クリックすると、具体的な処理進捗がダイアログで表示されます。")
        self.now_process_ctrl.Bind(wx.EVT_LEFT_DOWN, self.show_process_dialog)
        status_sizer.Add(self.now_process_ctrl, 0, wx.ALIGN_LEFT, 5)

        self.slash_ctrl = wx.TextCtrl(
            self, wx.ID_ANY, "/", wx.DefaultPosition, wx.Size(5, -1),
            wx.TE_READONLY | wx.BORDER_NONE | wx.WANTS_CHARS)
        self.slash_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        status_sizer.Add(self.slash_ctrl, 0, wx.ALIGN_LEFT, 5)

        self.total_process_ctrl = StatusCtrl(
            self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition,
            wx.Size(20, -1), wx.TE_READONLY | wx.BORDER_NONE | wx.WANTS_CHARS)
        self.total_process_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.total_process_ctrl.SetToolTip(
            u"全体の大まかな処理数です。クリックすると、具体的な処理進捗がダイアログで表示されます。")
        self.total_process_ctrl.Bind(wx.EVT_LEFT_DOWN,
                                     self.show_process_dialog)
        status_sizer.Add(self.total_process_ctrl, 0, wx.ALIGN_LEFT, 5)

        self.after_bracket_ctrl = wx.TextCtrl(
            self, wx.ID_ANY, ")", wx.DefaultPosition, wx.Size(5, -1),
            wx.TE_READONLY | wx.BORDER_NONE | wx.WANTS_CHARS)
        self.after_bracket_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        status_sizer.Add(self.after_bracket_ctrl, 0, wx.ALIGN_LEFT, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.Size(550, -1), wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        status_sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.sizer.Add(status_sizer, 0, wx.ALL, 0)

        self.fit()
Ejemplo n.º 8
0
    def __init__(self, frame: wx.Frame, noise: wx.Notebook, tab_idx: int):
        super().__init__(frame, noise, tab_idx)
        self.timer = wx.Timer(self, TIMER_ID)
        self.convert_noise_worker = None

        self.header_sizer = wx.BoxSizer(wx.VERTICAL)

        self.description_txt = wx.StaticText(self, wx.ID_ANY, u"モーションをゆらぎ(ノイズ)を付与して複製します。\n" \
                                             + "出力ファイル名のNxxは指定ゆらぎの大きさ、nxxxは複製連番、axxはやる気係数(身体の振りの大きさ)です。", wx.DefaultPosition, wx.DefaultSize, 0)
        self.header_sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                           wx.DefaultSize, wx.LI_HORIZONTAL)
        self.header_sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5)

        # 対象VMDファイルコントロール
        self.noise_vmd_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"対象モーションVMD/VPD", u"対象モーションVMD/VPDファイルを開く", ("vmd", "vpd"), wx.FLP_DEFAULT_STYLE, \
                                                         u"調整したい対象モーションのVMDパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                         file_model_spacer=46, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="noise_vmd", is_change_output=True, \
                                                         is_aster=False, is_save=False, set_no=1)
        self.header_sizer.Add(self.noise_vmd_file_ctrl.sizer, 1, wx.EXPAND, 0)

        # 出力先VMDファイルコントロール
        self.output_noise_vmd_file_ctrl = BaseFilePickerCtrl(frame, self, u"出力対象VMD", u"出力対象VMDファイルを開く", ("vmd"), wx.FLP_OVERWRITE_PROMPT | wx.FLP_SAVE | wx.FLP_USE_TEXTCTRL, \
                                                             u"調整結果の対象VMD出力パスを指定してください。\n対象VMDファイル名に基づいて自動生成されますが、任意のパスに変更することも可能です。", \
                                                             is_aster=False, is_save=True, set_no=1)
        self.header_sizer.Add(self.output_noise_vmd_file_ctrl.sizer, 1,
                              wx.EXPAND, 0)

        self.sizer.Add(self.header_sizer, 0, wx.EXPAND | wx.ALL, 5)

        self.setting_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # ゆらぎの大きさ
        self.noise_size_txt = wx.StaticText(self, wx.ID_ANY, u"ゆらぎの大きさ",
                                            wx.DefaultPosition, wx.DefaultSize,
                                            0)
        self.setting_sizer.Add(self.noise_size_txt, 0, wx.ALL, 5)

        self.noise_size_ctrl = wx.SpinCtrl(self,
                                           id=wx.ID_ANY,
                                           size=wx.Size(60, -1),
                                           value="8",
                                           min=0,
                                           max=99999999,
                                           initial=8)
        self.noise_size_ctrl.SetToolTip(
            u"ゆらぎの大きさを指定して下さい。値が大きいほど、ゆらぎが大きくなります。")
        self.noise_size_ctrl.Bind(wx.EVT_SPINCTRL, self.on_change_file)
        self.setting_sizer.Add(self.noise_size_ctrl, 0, wx.ALL, 5)

        # 複製数
        self.copy_cnt_txt = wx.StaticText(self, wx.ID_ANY, u"複製数",
                                          wx.DefaultPosition, wx.DefaultSize,
                                          0)
        self.setting_sizer.Add(self.copy_cnt_txt, 0, wx.ALL, 5)

        self.copy_cnt_ctrl = wx.SpinCtrl(self,
                                         id=wx.ID_ANY,
                                         size=wx.Size(60, -1),
                                         value="2",
                                         min=1,
                                         max=99999999,
                                         initial=2)
        self.copy_cnt_ctrl.SetToolTip(u"複製する数を指定して下さい。")
        self.copy_cnt_ctrl.Bind(wx.EVT_SPINCTRL, self.on_change_file)
        self.setting_sizer.Add(self.copy_cnt_ctrl, 0, wx.ALL, 5)

        # やる気係数
        self.motivation_flg_ctrl = wx.CheckBox(self, wx.ID_ANY, u"やる気係数を適用する",
                                               wx.DefaultPosition,
                                               wx.DefaultSize, 0)
        self.motivation_flg_ctrl.SetToolTip(
            u"チェックを入れると、やる気係数もランダムに発生します。\nやる気係数は値が大きいほどモーションの振り幅が大きくなります。\n値が小さいほどモーションの振り幅が小さくなります。"
        )
        self.setting_sizer.Add(self.motivation_flg_ctrl, 0, wx.ALL, 5)

        # 指ゆらぎ
        self.finger_noise_flg_ctrl = wx.CheckBox(self, wx.ID_ANY,
                                                 u"指にもゆらぎを適用する",
                                                 wx.DefaultPosition,
                                                 wx.DefaultSize, 0)
        self.finger_noise_flg_ctrl.SetToolTip(
            u"チェックを入れると、「指」をボーン名に含むボーンも揺らがせます。")
        self.setting_sizer.Add(self.finger_noise_flg_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(self.setting_sizer, 0, wx.EXPAND | wx.ALL, 5)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 実行ボタン
        self.noise_btn_ctrl = wx.Button(self, wx.ID_ANY,
                                        u"ゆらぎ複製", wx.DefaultPosition,
                                        wx.Size(200, 50), 0)
        self.noise_btn_ctrl.SetToolTip(u"ゆらぎを付与したモーションを複製します")
        self.noise_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_convert_noise)
        self.noise_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        btn_sizer.Add(self.noise_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.Layout()
        self.fit()

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_SMOOTH_THREAD, self.on_convert_noise_result)
Ejemplo n.º 9
0
class MultiJoinPanel(BasePanel):
    def __init__(self, frame: wx.Frame, multi_join: wx.Notebook, tab_idx: int):
        super().__init__(frame, multi_join, tab_idx)
        self.timer = wx.Timer(self, TIMER_ID)
        self.convert_multi_join_worker = None

        self.header_sizer = wx.BoxSizer(wx.VERTICAL)

        self.description_txt = wx.StaticText(self, wx.ID_ANY, u"モーションの指定ボーンの移動量XYZと回転量XYZを統合します。\n" \
                                             + "統合するボーンは「ボーン指定」ボタンから定義できます。多段分割の設定エクスポートファイル流用可能です。" \
                                             + "\n不要キー削除を行うと、キーが間引きされます。キー間がオリジナルから多少ずれ、やや時間がかかります。", wx.DefaultPosition, wx.DefaultSize, 0)
        self.header_sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                           wx.DefaultSize, wx.LI_HORIZONTAL)
        self.header_sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5)

        # 対象VMDファイルコントロール
        self.vmd_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"対象モーションVMD/VPD", u"対象モーションVMDファイルを開く", ("vmd", "vpd"), wx.FLP_DEFAULT_STYLE, \
                                                   u"調整したい対象モーションのVMDパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                   file_model_spacer=46, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="multi_join_vmd", is_change_output=True, \
                                                   is_aster=False, is_save=False, set_no=1)
        self.vmd_file_ctrl.file_ctrl.Bind(wx.EVT_FILEPICKER_CHANGED,
                                          self.on_change_file)
        self.header_sizer.Add(self.vmd_file_ctrl.sizer, 1, wx.EXPAND, 0)

        # 対象PMXファイルコントロール
        self.model_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"多段化済みモデルPMX", u"多段化済みモデルPMXファイルを開く", ("pmx"), wx.FLP_DEFAULT_STYLE, \
                                                     u"モーションを適用したい多段化済みモデルのPMXパスを指定してください。\n人体モデル以外にも適用可能です。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                     file_model_spacer=49, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="multi_join_pmx", \
                                                     is_change_output=True, is_aster=False, is_save=False, set_no=1)
        self.model_file_ctrl.file_ctrl.Bind(wx.EVT_FILEPICKER_CHANGED,
                                            self.on_change_file)
        self.header_sizer.Add(self.model_file_ctrl.sizer, 1, wx.EXPAND, 0)

        # 出力先VMDファイルコントロール
        self.output_vmd_file_ctrl = BaseFilePickerCtrl(frame, self, u"出力対象VMD", u"出力対象VMDファイルを開く", ("vmd"), wx.FLP_OVERWRITE_PROMPT | wx.FLP_SAVE | wx.FLP_USE_TEXTCTRL, \
                                                       u"調整結果の対象VMD出力パスを指定してください。\n対象VMDファイル名に基づいて自動生成されますが、任意のパスに変更することも可能です。", \
                                                       is_aster=False, is_save=True, set_no=1)
        self.header_sizer.Add(self.output_vmd_file_ctrl.sizer, 1, wx.EXPAND, 0)

        self.sizer.Add(self.header_sizer, 0, wx.EXPAND | wx.ALL, 5)

        self.setting_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # ボーン名指定
        self.bone_target_txt_ctrl = wx.TextCtrl(
            self, wx.ID_ANY, "", wx.DefaultPosition, (450, 50),
            wx.HSCROLL | wx.VSCROLL | wx.TE_MULTILINE | wx.TE_READONLY)
        self.bone_target_txt_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.setting_sizer.Add(self.bone_target_txt_ctrl, 1,
                               wx.EXPAND | wx.ALL, 5)

        self.bone_target_btn_ctrl = wx.Button(self, wx.ID_ANY, u"ボーン指定",
                                              wx.DefaultPosition,
                                              wx.DefaultSize, 0)
        self.bone_target_btn_ctrl.SetToolTip(
            u"モーションに登録されているボーンから、統合したいボーンを指定できます")
        self.bone_target_btn_ctrl.Bind(wx.EVT_BUTTON,
                                       self.on_click_bone_target)
        self.setting_sizer.Add(self.bone_target_btn_ctrl, 0,
                               wx.ALIGN_BOTTOM | wx.ALL, 5)

        self.sizer.Add(self.setting_sizer, 0, wx.EXPAND | wx.ALL, 5)

        # 不要キー削除処理
        self.flg_sizer = wx.BoxSizer(wx.VERTICAL)
        self.remove_unnecessary_flg_ctrl = wx.CheckBox(self, wx.ID_ANY,
                                                       u"不要キー削除処理を追加実行する",
                                                       wx.DefaultPosition,
                                                       wx.DefaultSize, 0)
        self.remove_unnecessary_flg_ctrl.SetToolTip(
            u"チェックを入れると、不要キー削除処理を追加で実行します。キーが減る分、キー間が少しズレる事があります。")
        self.remove_unnecessary_flg_ctrl.Bind(wx.EVT_CHECKBOX,
                                              self.on_change_file)
        self.flg_sizer.Add(self.remove_unnecessary_flg_ctrl, 0, wx.ALL, 5)
        self.sizer.Add(self.flg_sizer, 0, wx.EXPAND | wx.ALL, 5)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 実行ボタン
        self.multi_join_btn_ctrl = wx.Button(self, wx.ID_ANY, u"多段統合",
                                             wx.DefaultPosition,
                                             wx.Size(200, 50), 0)
        self.multi_join_btn_ctrl.SetToolTip(u"キーフレを多段用に統合したモーションを生成します")
        self.multi_join_btn_ctrl.Bind(wx.EVT_LEFT_DOWN,
                                      self.on_convert_multi_join)
        self.multi_join_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        btn_sizer.Add(self.multi_join_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.Layout()
        self.fit()

        # ボーン選択用ダイアログ
        self.bone_dialog = TargetBoneDialog(self.frame, self, "←", "分割")

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_SMOOTH_THREAD, self.on_convert_multi_join_result)

    def on_click_bone_target(self, event: wx.Event):
        self.disable()

        # VMD読み込み
        sys.stdout = self.console_ctrl
        self.vmd_file_ctrl.load()
        # PMX読み込み
        self.model_file_ctrl.load()

        if (self.vmd_file_ctrl.data and self.model_file_ctrl.data and \
                (self.vmd_file_ctrl.data.digest != self.bone_dialog.vmd_digest or self.model_file_ctrl.data.digest != self.bone_dialog.pmx_digest)):

            # データが揃ってたら押下可能
            self.bone_target_btn_ctrl.Enable()
            # リストクリア
            self.bone_target_txt_ctrl.SetValue("")
            # ボーン選択用ダイアログ
            self.bone_dialog.Destroy()
            self.bone_dialog = TargetBoneDialog(self.frame, self, "←", "統合")
            self.bone_dialog.initialize()
        else:
            if not self.vmd_file_ctrl.data or not self.model_file_ctrl.data:
                logger.error("対象モーションVMD/VPDもしくは多段化済みモデルPMXが未指定です。",
                             decoration=MLogger.DECORATION_BOX)
                self.enable()
                return

        self.enable()

        if self.bone_dialog.ShowModal() == wx.ID_CANCEL:
            return  # the user changed their mind

        # 選択されたボーンリストを入力欄に設定
        bone_list = self.bone_dialog.get_bone_list()

        selections = ["{0} {7} 【回転X: {1}】【回転Y: {2}】【回転Z: {3}】【移動X: {4}】【移動Y: {5}】【移動Z: {6}】" \
                      .format(bset[0], bset[1], bset[2], bset[3], bset[4], bset[5], bset[6], "←") for bset in bone_list]
        self.bone_target_txt_ctrl.SetValue('\n'.join(selections))

        self.bone_dialog.Hide()

    # ファイル変更時の処理
    def on_change_file(self, event: wx.Event):
        self.set_output_vmd_path(event, is_force=True)

    def set_output_vmd_path(self, event: wx.Event, is_force=False):
        output_multi_join_vmd_path = MFileUtils.get_output_multi_join_vmd_path(
            self.vmd_file_ctrl.file_ctrl.GetPath(),
            self.model_file_ctrl.file_ctrl.GetPath(),
            self.output_vmd_file_ctrl.file_ctrl.GetPath(), is_force)

        self.output_vmd_file_ctrl.file_ctrl.SetPath(output_multi_join_vmd_path)

        if len(output_multi_join_vmd_path) >= 255 and os.name == "nt":
            logger.error("生成予定のファイルパスがWindowsの制限を超えています。\n生成予定パス: {0}".format(
                output_multi_join_vmd_path),
                         decoration=MLogger.DECORATION_BOX)

    # フォーム無効化
    def disable(self):
        self.vmd_file_ctrl.disable()
        self.model_file_ctrl.disable()
        self.output_vmd_file_ctrl.disable()
        self.bone_target_btn_ctrl.Disable()
        self.multi_join_btn_ctrl.Disable()
        self.remove_unnecessary_flg_ctrl.Disable()

    # フォーム無効化
    def enable(self):
        self.vmd_file_ctrl.enable()
        self.model_file_ctrl.enable()
        self.output_vmd_file_ctrl.enable()
        self.bone_target_btn_ctrl.Enable()
        self.multi_join_btn_ctrl.Enable()
        self.remove_unnecessary_flg_ctrl.Enable()

    def on_doubleclick(self, event: wx.Event):
        self.timer.Stop()
        logger.warning("ダブルクリックされました。", decoration=MLogger.DECORATION_BOX)
        event.Skip(False)
        return False

    def on_convert_multi_join(self, event: wx.Event):
        self.timer.Start(200)
        self.Bind(wx.EVT_TIMER, self.on_convert, id=TIMER_ID)

    # 多段統合変換
    def on_convert(self, event: wx.Event):
        self.timer.Stop()
        self.Unbind(wx.EVT_TIMER, id=TIMER_ID)
        # フォーム無効化
        self.disable()
        # タブ固定
        self.fix_tab()
        # コンソールクリア
        self.console_ctrl.Clear()
        # 出力先を多段統合パネルのコンソールに変更
        sys.stdout = self.console_ctrl

        self.vmd_file_ctrl.save()
        self.model_file_ctrl.save()

        # JSON出力
        MFileUtils.save_history(self.frame.mydir_path,
                                self.frame.file_hitories)

        self.elapsed_time = 0
        result = True
        result = self.vmd_file_ctrl.is_valid() and result
        result = self.model_file_ctrl.is_valid() and result

        if len(self.bone_target_txt_ctrl.GetValue()) == 0:
            logger.error("統合対象ボーンが指定されていません。",
                         decoration=MLogger.DECORATION_BOX)
            result = False

        if not result:
            # 終了音
            self.frame.sound_finish()
            # タブ移動可
            self.release_tab()
            # フォーム有効化
            self.enable()

            return result

        # 多段統合変換開始
        if self.multi_join_btn_ctrl.GetLabel(
        ) == "多段統合停止" and self.convert_multi_join_worker:
            # フォーム無効化
            self.disable()
            # 停止状態でボタン押下時、停止
            self.convert_multi_join_worker.stop()

            # タブ移動可
            self.frame.release_tab()
            # フォーム有効化
            self.frame.enable()
            # ワーカー終了
            self.convert_multi_join_worker = None
            # プログレス非表示
            self.gauge_ctrl.SetValue(0)

            logger.warning("多段統合を中断します。", decoration=MLogger.DECORATION_BOX)
            self.multi_join_btn_ctrl.SetLabel("多段統合")

            event.Skip(False)
        elif not self.convert_multi_join_worker:
            # フォーム無効化
            self.disable()
            # タブ固定
            self.fix_tab()
            # コンソールクリア
            self.console_ctrl.Clear()
            # ラベル変更
            self.multi_join_btn_ctrl.SetLabel("多段統合停止")
            self.multi_join_btn_ctrl.Enable()

            self.convert_multi_join_worker = MultiJoinWorkerThread(
                self.frame, MultiJoinThreadEvent, self.frame.is_saving,
                self.frame.is_out_log)
            self.convert_multi_join_worker.start()

            event.Skip()
        else:
            logger.error("まだ処理が実行中です。終了してから再度実行してください。",
                         decoration=MLogger.DECORATION_BOX)
            event.Skip(False)

        return result

    # 多段統合変換完了処理
    def on_convert_multi_join_result(self, event: wx.Event):
        self.elapsed_time = event.elapsed_time
        logger.info("\n処理時間: %s", self.show_worked_time())
        self.multi_join_btn_ctrl.SetLabel("多段統合")

        # 終了音
        self.frame.sound_finish()

        # タブ移動可
        self.release_tab()
        # フォーム有効化
        self.enable()
        # ワーカー終了
        self.convert_multi_join_worker = None
        # プログレス非表示
        self.gauge_ctrl.SetValue(0)
        # ラベル変更
        self.multi_join_btn_ctrl.SetLabel("多段統合")
        self.multi_join_btn_ctrl.Enable()

    def show_worked_time(self):
        # 経過秒数を時分秒に変換
        td_m, td_s = divmod(self.elapsed_time, 60)

        if td_m == 0:
            worked_time = "{0:02d}秒".format(int(td_s))
        else:
            worked_time = "{0:02d}分{1:02d}秒".format(int(td_m), int(td_s))

        return worked_time
Ejemplo n.º 10
0
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int):
        super().__init__(frame, parent, tab_idx)
        self.blend_worker = None

        self.description_txt = wx.StaticText(self, wx.ID_ANY, "指定されたPMXファイルのモーフをランダムに変化させた結果を、VMDファイルとして出力します。\n" \
                                             + "モーフの組み合わせが多くなると破綻する確率が高くなりますので、その状態での一般公開は避けてください。",
                                             wx.DefaultPosition, wx.DefaultSize, 0)
        self.sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
        self.sizer.Add(self.static_line, 0, wx.EXPAND | wx.ALL, 5)

        # PMXファイルコントロール
        self.pmx_file_ctrl = LoadFilePickerCtrl(frame, self, u"PMXファイル", u"PMXファイルを開く", ("pmx"), wx.FLP_DEFAULT_STYLE, \
                                                u"モーフをブレンドさせたいPMXのパスを指定してください。\nD&Dでの指定、開くボタンからの指定ができます。\nパスを指定すると下部欄にモーフリストが表示されます。", \
                                                is_aster=False, is_save=False, set_no=0)
        self.pmx_file_ctrl.file_ctrl.Bind(wx.EVT_FILEPICKER_CHANGED, self.on_change_file)
        self.sizer.Add(self.pmx_file_ctrl.sizer, 0, wx.EXPAND, 0)

        # モーフ選択欄 ---------
        self.morph_sizer = wx.BoxSizer(wx.HORIZONTAL)

        MORPH_TOOLTIP = "ブレンド対象となるモーフを選択して下さい。"

        self.morph_eye_txt = wx.StaticText(self, wx.ID_ANY, "目")
        self.morph_eye_txt.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_eye_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_eye_list = wx.ListBox(self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(110, 100), choices=[], style=wx.LB_MULTIPLE | wx.LB_ALWAYS_SB)
        self.morph_eye_list.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_eye_list, 0, wx.ALL, 5)

        self.morph_eyebrow_txt = wx.StaticText(self, wx.ID_ANY, "眉")
        self.morph_eyebrow_txt.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_eyebrow_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_eyebrow_list = wx.ListBox(self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(110, 100), choices=[], style=wx.LB_MULTIPLE | wx.LB_ALWAYS_SB)
        self.morph_eyebrow_list.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_eyebrow_list, 0, wx.ALL, 5)

        self.morph_lip_txt = wx.StaticText(self, wx.ID_ANY, "口")
        self.morph_lip_txt.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_lip_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_lip_list = wx.ListBox(self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(110, 100), choices=[], style=wx.LB_MULTIPLE | wx.LB_ALWAYS_SB)
        self.morph_lip_list.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_lip_list, 0, wx.ALL, 5)

        self.morph_other_txt = wx.StaticText(self, wx.ID_ANY, "他")
        self.morph_other_txt.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_other_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_other_list = wx.ListBox(self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(110, 100), choices=[], style=wx.LB_MULTIPLE | wx.LB_ALWAYS_SB)
        self.morph_other_list.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_other_list, 0, wx.ALL, 5)

        self.sizer.Add(self.morph_sizer, 0, wx.EXPAND | wx.ALL, 0)

        # モーフ増減値 ---------

        self.value_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # モーフ最小値
        self.morph_value_min_txt = wx.StaticText(self, wx.ID_ANY, u"最小値", wx.DefaultPosition, wx.DefaultSize, 0)
        self.morph_value_min_txt.SetToolTip(u"モーフ増減の最小値です。-10から10の間で設定できます。(小数点可)")
        self.value_sizer.Add(self.morph_value_min_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_spin_min = wx.SpinCtrlDouble(self, id=wx.ID_ANY, size=wx.Size(80, -1), min=-10, max=10, initial=0.0, inc=0.1)
        self.morph_spin_min.SetToolTip(u"モーフ増減の最小値です。-10から10の間で設定できます。(小数点可)")
        self.morph_spin_min.Bind(wx.EVT_MOUSEWHEEL, lambda event: self.frame.on_wheel_spin_ctrl(event, 0.1))
        self.value_sizer.Add(self.morph_spin_min, 0, wx.ALL, 5)

        # モーフ最大値
        self.morph_value_max_txt = wx.StaticText(self, wx.ID_ANY, u"最大値", wx.DefaultPosition, wx.DefaultSize, 0)
        self.morph_value_max_txt.SetToolTip(u"モーフ増減の最大値です。-10から10の間で設定できます。(小数点可)")
        self.value_sizer.Add(self.morph_value_max_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_spin_max = wx.SpinCtrlDouble(self, id=wx.ID_ANY, size=wx.Size(80, -1), min=-10, max=10, initial=0.6, inc=0.1)
        self.morph_spin_max.SetToolTip(u"モーフ増減の最大値です。-10から10の間で設定できます。(小数点可)")
        self.morph_spin_max.Bind(wx.EVT_MOUSEWHEEL, lambda event: self.frame.on_wheel_spin_ctrl(event, 0.1))
        self.value_sizer.Add(self.morph_spin_max, 0, wx.ALL, 5)

        # モーフ増加値
        self.morph_value_inc_txt = wx.StaticText(self, wx.ID_ANY, u"増加値", wx.DefaultPosition, wx.DefaultSize, 0)
        self.morph_value_inc_txt.SetToolTip(u"モーフ増減の増加量です。この増加量分ごとにモーフ組み合わせを生成していきます。0から1の間で設定できます。(小数点可)")
        self.morph_value_inc_txt.Wrap(-1)
        self.value_sizer.Add(self.morph_value_inc_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_spin_inc = wx.SpinCtrlDouble(self, id=wx.ID_ANY, size=wx.Size(80, -1), min=0.01, max=1, initial=0.1, inc=0.05)
        self.morph_spin_inc.SetToolTip(u"モーフ増減の増加量です。この増加量分ごとにモーフ組み合わせを生成していきます。0から1の間で設定できます。(小数点可)")
        self.morph_spin_inc.Bind(wx.EVT_MOUSEWHEEL, lambda event: self.frame.on_wheel_spin_ctrl(event, 0.05))
        self.value_sizer.Add(self.morph_spin_inc, 0, wx.ALL, 5)

        self.sizer.Add(self.value_sizer, 0, wx.EXPAND | wx.ALL, 0)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # モーフブレンド実行ボタン
        self.blend_btn_ctrl = wx.Button(self, wx.ID_ANY, u"モーフブレンド実行", wx.DefaultPosition, wx.Size(200, 50), 0)
        self.blend_btn_ctrl.SetToolTip(u"モーフをブレンドしたVMDを生成します。")
        self.blend_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_convert_blend)
        btn_sizer.Add(self.blend_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(wx.EVT_CHAR, lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.fit()

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_CSV_THREAD, self.on_convert_blend_result)
Ejemplo n.º 11
0
class BlendPanel(BasePanel):
    
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int):
        super().__init__(frame, parent, tab_idx)
        self.blend_worker = None

        self.description_txt = wx.StaticText(self, wx.ID_ANY, "指定されたPMXファイルのモーフをランダムに変化させた結果を、VMDファイルとして出力します。\n" \
                                             + "モーフの組み合わせが多くなると破綻する確率が高くなりますので、その状態での一般公開は避けてください。",
                                             wx.DefaultPosition, wx.DefaultSize, 0)
        self.sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
        self.sizer.Add(self.static_line, 0, wx.EXPAND | wx.ALL, 5)

        # PMXファイルコントロール
        self.pmx_file_ctrl = LoadFilePickerCtrl(frame, self, u"PMXファイル", u"PMXファイルを開く", ("pmx"), wx.FLP_DEFAULT_STYLE, \
                                                u"モーフをブレンドさせたいPMXのパスを指定してください。\nD&Dでの指定、開くボタンからの指定ができます。\nパスを指定すると下部欄にモーフリストが表示されます。", \
                                                is_aster=False, is_save=False, set_no=0)
        self.pmx_file_ctrl.file_ctrl.Bind(wx.EVT_FILEPICKER_CHANGED, self.on_change_file)
        self.sizer.Add(self.pmx_file_ctrl.sizer, 0, wx.EXPAND, 0)

        # モーフ選択欄 ---------
        self.morph_sizer = wx.BoxSizer(wx.HORIZONTAL)

        MORPH_TOOLTIP = "ブレンド対象となるモーフを選択して下さい。"

        self.morph_eye_txt = wx.StaticText(self, wx.ID_ANY, "目")
        self.morph_eye_txt.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_eye_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_eye_list = wx.ListBox(self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(110, 100), choices=[], style=wx.LB_MULTIPLE | wx.LB_ALWAYS_SB)
        self.morph_eye_list.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_eye_list, 0, wx.ALL, 5)

        self.morph_eyebrow_txt = wx.StaticText(self, wx.ID_ANY, "眉")
        self.morph_eyebrow_txt.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_eyebrow_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_eyebrow_list = wx.ListBox(self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(110, 100), choices=[], style=wx.LB_MULTIPLE | wx.LB_ALWAYS_SB)
        self.morph_eyebrow_list.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_eyebrow_list, 0, wx.ALL, 5)

        self.morph_lip_txt = wx.StaticText(self, wx.ID_ANY, "口")
        self.morph_lip_txt.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_lip_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_lip_list = wx.ListBox(self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(110, 100), choices=[], style=wx.LB_MULTIPLE | wx.LB_ALWAYS_SB)
        self.morph_lip_list.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_lip_list, 0, wx.ALL, 5)

        self.morph_other_txt = wx.StaticText(self, wx.ID_ANY, "他")
        self.morph_other_txt.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_other_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_other_list = wx.ListBox(self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(110, 100), choices=[], style=wx.LB_MULTIPLE | wx.LB_ALWAYS_SB)
        self.morph_other_list.SetToolTip(MORPH_TOOLTIP)
        self.morph_sizer.Add(self.morph_other_list, 0, wx.ALL, 5)

        self.sizer.Add(self.morph_sizer, 0, wx.EXPAND | wx.ALL, 0)

        # モーフ増減値 ---------

        self.value_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # モーフ最小値
        self.morph_value_min_txt = wx.StaticText(self, wx.ID_ANY, u"最小値", wx.DefaultPosition, wx.DefaultSize, 0)
        self.morph_value_min_txt.SetToolTip(u"モーフ増減の最小値です。-10から10の間で設定できます。(小数点可)")
        self.value_sizer.Add(self.morph_value_min_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_spin_min = wx.SpinCtrlDouble(self, id=wx.ID_ANY, size=wx.Size(80, -1), min=-10, max=10, initial=0.0, inc=0.1)
        self.morph_spin_min.SetToolTip(u"モーフ増減の最小値です。-10から10の間で設定できます。(小数点可)")
        self.morph_spin_min.Bind(wx.EVT_MOUSEWHEEL, lambda event: self.frame.on_wheel_spin_ctrl(event, 0.1))
        self.value_sizer.Add(self.morph_spin_min, 0, wx.ALL, 5)

        # モーフ最大値
        self.morph_value_max_txt = wx.StaticText(self, wx.ID_ANY, u"最大値", wx.DefaultPosition, wx.DefaultSize, 0)
        self.morph_value_max_txt.SetToolTip(u"モーフ増減の最大値です。-10から10の間で設定できます。(小数点可)")
        self.value_sizer.Add(self.morph_value_max_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_spin_max = wx.SpinCtrlDouble(self, id=wx.ID_ANY, size=wx.Size(80, -1), min=-10, max=10, initial=0.6, inc=0.1)
        self.morph_spin_max.SetToolTip(u"モーフ増減の最大値です。-10から10の間で設定できます。(小数点可)")
        self.morph_spin_max.Bind(wx.EVT_MOUSEWHEEL, lambda event: self.frame.on_wheel_spin_ctrl(event, 0.1))
        self.value_sizer.Add(self.morph_spin_max, 0, wx.ALL, 5)

        # モーフ増加値
        self.morph_value_inc_txt = wx.StaticText(self, wx.ID_ANY, u"増加値", wx.DefaultPosition, wx.DefaultSize, 0)
        self.morph_value_inc_txt.SetToolTip(u"モーフ増減の増加量です。この増加量分ごとにモーフ組み合わせを生成していきます。0から1の間で設定できます。(小数点可)")
        self.morph_value_inc_txt.Wrap(-1)
        self.value_sizer.Add(self.morph_value_inc_txt, 0, wx.EXPAND | wx.ALL, 5)

        self.morph_spin_inc = wx.SpinCtrlDouble(self, id=wx.ID_ANY, size=wx.Size(80, -1), min=0.01, max=1, initial=0.1, inc=0.05)
        self.morph_spin_inc.SetToolTip(u"モーフ増減の増加量です。この増加量分ごとにモーフ組み合わせを生成していきます。0から1の間で設定できます。(小数点可)")
        self.morph_spin_inc.Bind(wx.EVT_MOUSEWHEEL, lambda event: self.frame.on_wheel_spin_ctrl(event, 0.05))
        self.value_sizer.Add(self.morph_spin_inc, 0, wx.ALL, 5)

        self.sizer.Add(self.value_sizer, 0, wx.EXPAND | wx.ALL, 0)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # モーフブレンド実行ボタン
        self.blend_btn_ctrl = wx.Button(self, wx.ID_ANY, u"モーフブレンド実行", wx.DefaultPosition, wx.Size(200, 50), 0)
        self.blend_btn_ctrl.SetToolTip(u"モーフをブレンドしたVMDを生成します。")
        self.blend_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_convert_blend)
        btn_sizer.Add(self.blend_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(wx.EVT_CHAR, lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.fit()

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_CSV_THREAD, self.on_convert_blend_result)

    def on_change_file(self, event: wx.Event):
        # フォーム無効化
        self.disable()
        # 出力先をCSVパネルのコンソールに変更
        sys.stdout = self.console_ctrl

        # ファイルコントロール自身のパス確定処理
        # 先頭と末尾の改行は除去
        target_path = self.pmx_file_ctrl.file_ctrl.GetPath().strip()
        logger.test("target_path strip: %s", target_path)

        # 先頭と末尾のダブルクォーテーションは除去
        target_path = re.sub(r'^\\+\"(\w)\\', r'\1:\\', target_path)
        target_path = target_path.strip("\"")
        logger.test("target_path strip: %s", target_path)

        # 再設定
        self.pmx_file_ctrl.file_ctrl.SetPath(target_path)

        # ファイル読み込み処理
        if self.pmx_file_ctrl.is_valid() and self.pmx_file_ctrl.load(is_check=False):
            # モーフ展開処理
            morph_names = {"目": [], "眉": [], "口": [], "他": []}

            for mk, mv in self.pmx_file_ctrl.data.morphs.items():
                if mv.display:
                    morph_names[mv.get_panel_name()].append(mk)

            self.morph_eye_list.SetItems(morph_names["目"])
            self.morph_eyebrow_list.SetItems(morph_names["眉"])
            self.morph_lip_list.SetItems(morph_names["口"])
            self.morph_other_list.SetItems(morph_names["他"])

        # フォーム有効化
        self.enable()

        event.Skip()

    # フォーム無効化
    def disable(self):
        self.pmx_file_ctrl.disable()
        self.morph_eye_list.Disable()
        self.morph_eyebrow_list.Disable()
        self.morph_lip_list.Disable()
        self.morph_other_list.Disable()
        self.morph_spin_min.Disable()
        self.morph_spin_max.Disable()
        self.morph_spin_inc.Disable()
        self.blend_btn_ctrl.Disable()

    # フォーム無効化
    def enable(self):
        self.pmx_file_ctrl.enable()
        self.morph_eye_list.Enable()
        self.morph_eyebrow_list.Enable()
        self.morph_lip_list.Enable()
        self.morph_other_list.Enable()
        self.morph_spin_min.Enable()
        self.morph_spin_max.Enable()
        self.morph_spin_inc.Enable()
        self.blend_btn_ctrl.Enable()

    # モーフブレンド
    def on_convert_blend(self, event: wx.Event):
        # フォーム無効化
        self.disable()
        # タブ固定
        self.fix_tab()
        # コンソールクリア
        self.console_ctrl.Clear()
        # 出力先をブレンドパネルのコンソールに変更
        sys.stdout = self.console_ctrl

        wx.GetApp().Yield()

        self.elapsed_time = 0
        result = True
        result = self.pmx_file_ctrl.is_valid() and result
        result = self.is_valid() and result

        if not result:
            # 終了音
            self.frame.sound_finish()
            # タブ移動可
            self.release_tab()
            # フォーム有効化
            self.enable()
            # 出力先をデフォルトに戻す
            sys.stdout = self.frame.file_panel_ctrl.console_ctrl

            return result

        # モーフブレンド開始
        if self.blend_worker:
            logger.error("まだ処理が実行中です。終了してから再度実行してください。", decoration=MLogger.DECORATION_BOX)
        else:
            # 別スレッドで実行
            self.blend_worker = BlendWorkerThread(self.frame, BlendThreadEvent)
            self.blend_worker.start()

        return result

        event.Skip()

    # モーフブレンド完了処理
    def on_convert_blend_result(self, event: wx.Event):
        self.elapsed_time = event.elapsed_time

        # 終了音
        self.frame.sound_finish()

        # タブ移動可
        self.release_tab()
        # フォーム有効化
        self.enable()
        # ワーカー終了
        self.blend_worker = None
        # プログレス非表示
        self.gauge_ctrl.SetValue(0)

        if not event.result:
            logger.error("モーフブレンド処理に失敗しました。", decoration=MLogger.DECORATION_BOX)
            
            event.Skip()
            return False

        logger.info("モーフブレンドが完了しました", decoration=MLogger.DECORATION_BOX, title="OK")

        # 出力先をデフォルトに戻す
        sys.stdout = self.frame.file_panel_ctrl.console_ctrl

    def is_valid(self):
        if len(self.morph_eye_list.GetSelections()) + len(self.morph_eyebrow_list.GetSelections()) \
           + len(self.morph_lip_list.GetSelections()) + len(self.morph_other_list.GetSelections()) == 0:
            logger.warning("ブレント対象となるモーフが選択されていません", decoration=MLogger.DECORATION_BOX)
        
            return False
        
        return True
Ejemplo n.º 12
0
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int):
        super().__init__(frame, parent, tab_idx)
        self.convert_vmd_worker = None

        self.description_txt = wx.StaticText(self, wx.ID_ANY, "指定されたCSVファイル(ボーン+モーフ or カメラ)を、VMDファイルとして出力します。\n" \
                                             + "モデルモーション(ボーン・モーフ)とカメラモーション(カメラ)は別々に出力できます。\n" \
                                             + "CSVのフォーマットは、CSVタブで出力したデータと同じものを定義してください。", wx.DefaultPosition, wx.DefaultSize, 0)
        self.sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                         wx.DefaultSize, wx.LI_HORIZONTAL)
        self.sizer.Add(self.static_line, 0, wx.EXPAND | wx.ALL, 5)

        # CSVファイルコントロール(ボーン)
        self.bone_csv_file_ctrl = BaseFilePickerCtrl(frame, self, u"CSVファイル(ボーン)", u"CSVファイルを選択してください", ("csv"), wx.FLP_DEFAULT_STYLE, \
                                                     u"VMDに変換したいボーンモーションのファイルパスを指定してください。", \
                                                     is_aster=False, is_save=False, set_no=0, required=False)
        self.sizer.Add(self.bone_csv_file_ctrl.sizer, 0, wx.EXPAND | wx.ALL, 0)

        # CSVファイルコントロール(モーフ)
        self.morph_csv_file_ctrl = BaseFilePickerCtrl(frame, self, u"CSVファイル(モーフ)", u"CSVファイルを選択してください", ("csv"), wx.FLP_DEFAULT_STYLE, \
                                                      u"VMDに変換したいモーフモーションのファイルパスを指定してください。", \
                                                      is_aster=False, is_save=False, set_no=0, required=False)
        self.sizer.Add(self.morph_csv_file_ctrl.sizer, 0, wx.EXPAND | wx.ALL,
                       0)

        self.static_line2 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                          wx.DefaultSize, wx.LI_HORIZONTAL)
        self.sizer.Add(self.static_line2, 0, wx.EXPAND | wx.ALL, 5)

        # CSVファイルコントロール(カメラ)
        self.camera_csv_file_ctrl = BaseFilePickerCtrl(frame, self, u"CSVファイル(カメラ)", u"CSVファイルを選択してください", ("csv"), wx.FLP_DEFAULT_STYLE, \
                                                       u"VMDに変換したいカメラモーションのファイルパスを指定してください。", \
                                                       is_aster=False, is_save=False, set_no=0, required=False)
        self.sizer.Add(self.camera_csv_file_ctrl.sizer, 0, wx.EXPAND | wx.ALL,
                       0)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # VMD変換実行ボタン
        self.vmd_btn_ctrl = wx.Button(self, wx.ID_ANY, u"VMD変換実行",
                                      wx.DefaultPosition, wx.Size(200, 50), 0)
        self.vmd_btn_ctrl.SetToolTip(u"CSVをVMDに変換します。")
        self.vmd_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_convert_vmd)
        btn_sizer.Add(self.vmd_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.fit()

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_VMD_THREAD, self.on_convert_vmd_result)
Ejemplo n.º 13
0
class VmdPanel(BasePanel):
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int):
        super().__init__(frame, parent, tab_idx)
        self.convert_vmd_worker = None

        self.description_txt = wx.StaticText(self, wx.ID_ANY, "指定されたCSVファイル(ボーン+モーフ or カメラ)を、VMDファイルとして出力します。\n" \
                                             + "モデルモーション(ボーン・モーフ)とカメラモーション(カメラ)は別々に出力できます。\n" \
                                             + "CSVのフォーマットは、CSVタブで出力したデータと同じものを定義してください。", wx.DefaultPosition, wx.DefaultSize, 0)
        self.sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                         wx.DefaultSize, wx.LI_HORIZONTAL)
        self.sizer.Add(self.static_line, 0, wx.EXPAND | wx.ALL, 5)

        # CSVファイルコントロール(ボーン)
        self.bone_csv_file_ctrl = BaseFilePickerCtrl(frame, self, u"CSVファイル(ボーン)", u"CSVファイルを選択してください", ("csv"), wx.FLP_DEFAULT_STYLE, \
                                                     u"VMDに変換したいボーンモーションのファイルパスを指定してください。", \
                                                     is_aster=False, is_save=False, set_no=0, required=False)
        self.sizer.Add(self.bone_csv_file_ctrl.sizer, 0, wx.EXPAND | wx.ALL, 0)

        # CSVファイルコントロール(モーフ)
        self.morph_csv_file_ctrl = BaseFilePickerCtrl(frame, self, u"CSVファイル(モーフ)", u"CSVファイルを選択してください", ("csv"), wx.FLP_DEFAULT_STYLE, \
                                                      u"VMDに変換したいモーフモーションのファイルパスを指定してください。", \
                                                      is_aster=False, is_save=False, set_no=0, required=False)
        self.sizer.Add(self.morph_csv_file_ctrl.sizer, 0, wx.EXPAND | wx.ALL,
                       0)

        self.static_line2 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                          wx.DefaultSize, wx.LI_HORIZONTAL)
        self.sizer.Add(self.static_line2, 0, wx.EXPAND | wx.ALL, 5)

        # CSVファイルコントロール(カメラ)
        self.camera_csv_file_ctrl = BaseFilePickerCtrl(frame, self, u"CSVファイル(カメラ)", u"CSVファイルを選択してください", ("csv"), wx.FLP_DEFAULT_STYLE, \
                                                       u"VMDに変換したいカメラモーションのファイルパスを指定してください。", \
                                                       is_aster=False, is_save=False, set_no=0, required=False)
        self.sizer.Add(self.camera_csv_file_ctrl.sizer, 0, wx.EXPAND | wx.ALL,
                       0)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # VMD変換実行ボタン
        self.vmd_btn_ctrl = wx.Button(self, wx.ID_ANY, u"VMD変換実行",
                                      wx.DefaultPosition, wx.Size(200, 50), 0)
        self.vmd_btn_ctrl.SetToolTip(u"CSVをVMDに変換します。")
        self.vmd_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_convert_vmd)
        btn_sizer.Add(self.vmd_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.fit()

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_VMD_THREAD, self.on_convert_vmd_result)

    # フォーム無効化
    def disable(self):
        self.bone_csv_file_ctrl.disable()
        self.morph_csv_file_ctrl.disable()
        self.camera_csv_file_ctrl.disable()
        self.vmd_btn_ctrl.Disable()

    # フォーム無効化
    def enable(self):
        self.bone_csv_file_ctrl.enable()
        self.morph_csv_file_ctrl.enable()
        self.camera_csv_file_ctrl.enable()
        self.vmd_btn_ctrl.Enable()

    # VMD変換
    def on_convert_vmd(self, event: wx.Event):
        # フォーム無効化
        self.disable()
        # タブ固定
        self.fix_tab()
        # コンソールクリア
        self.console_ctrl.Clear()
        # 出力先をVMDパネルのコンソールに変更
        sys.stdout = self.console_ctrl

        wx.GetApp().Yield()

        self.elapsed_time = 0
        result = True
        result = self.bone_csv_file_ctrl.is_valid() and result

        if not result:
            # 終了音
            self.frame.sound_finish()
            # タブ移動可
            self.release_tab()
            # フォーム有効化
            self.enable()
            # 出力先をデフォルトに戻す
            if sys.stdout != self.frame.file_panel_ctrl.console_ctrl:
                sys.stdout = self.frame.file_panel_ctrl.console_ctrl

            return result

        # VMD変換開始
        if self.convert_vmd_worker:
            logger.error("まだ処理が実行中です。終了してから再度実行してください。",
                         decoration=MLogger.DECORATION_BOX)
        else:
            # 別スレッドで実行
            self.convert_vmd_worker = VmdWorkerThread(self.frame,
                                                      VmdThreadEvent)
            self.convert_vmd_worker.start()

        return result

        event.Skip()

    # VMD変換完了処理
    def on_convert_vmd_result(self, event: wx.Event):
        self.elapsed_time = event.elapsed_time

        # 終了音
        self.frame.sound_finish()

        # タブ移動可
        self.release_tab()
        # フォーム有効化
        self.enable()
        # ワーカー終了
        self.convert_vmd_worker = None
        # プログレス非表示
        self.gauge_ctrl.SetValue(0)

        if not event.result:
            logger.error("VMD変換処理に失敗しました。", decoration=MLogger.DECORATION_BOX)

            event.Skip()
            return False

        logger.info("VMD変換が完了しました",
                    decoration=MLogger.DECORATION_BOX,
                    title="OK")

        # 出力先をデフォルトに戻す
        if sys.stdout != self.frame.file_panel_ctrl.console_ctrl:
            sys.stdout = self.frame.file_panel_ctrl.console_ctrl
Ejemplo n.º 14
0
class SmoothPanel(BasePanel):
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int):
        super().__init__(frame, parent, tab_idx)
        self.convert_smooth_worker = None

        self.header_sizer = wx.BoxSizer(wx.VERTICAL)

        self.description_txt = wx.StaticText(self, wx.ID_ANY, u"指定されたVMDファイルに対して、キーを分割し、滑らかな補間曲線で繋いで、再出力します。\n" \
                                             + "スムージング回数1回で、全打ちとなり、2回目以降はフィルタリングした後に補間曲線で繋ぎます。\n" \
                                             + "不要キー削除を行うと、キーが間引きされます。キー間がオリジナルから多少ずれ、やや時間がかかります。", wx.DefaultPosition, wx.DefaultSize, 0)
        self.header_sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                           wx.DefaultSize, wx.LI_HORIZONTAL)
        self.header_sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5)

        # 対象VMDファイルコントロール
        self.smooth_vmd_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"対象モーションVMD", u"対象モーションVMDファイルを開く", ("vmd"), wx.FLP_DEFAULT_STYLE, \
                                                          u"調整したい対象モーションのVMDパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                          file_model_spacer=46, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="smooth_vmd", is_change_output=True, \
                                                          is_aster=False, is_save=False, set_no=1)
        self.header_sizer.Add(self.smooth_vmd_file_ctrl.sizer, 1, wx.EXPAND, 0)

        # 対象PMXファイルコントロール
        self.smooth_model_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"適用モデルPMX", u"適用モデルPMXファイルを開く", ("pmx"), wx.FLP_DEFAULT_STYLE, \
                                                            u"モーションを適用したいモデルのPMXパスを指定してください。\n人体モデル以外にも適用可能です。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                            file_model_spacer=52, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="smooth_pmx", \
                                                            is_change_output=True, is_aster=False, is_save=False, set_no=1)
        self.header_sizer.Add(self.smooth_model_file_ctrl.sizer, 1, wx.EXPAND,
                              0)

        # 出力先VMDファイルコントロール
        self.output_smooth_vmd_file_ctrl = BaseFilePickerCtrl(frame, self, u"出力対象VMD", u"出力対象VMDファイルを開く", ("vmd"), wx.FLP_OVERWRITE_PROMPT | wx.FLP_SAVE | wx.FLP_USE_TEXTCTRL, \
                                                              u"調整結果の対象VMD出力パスを指定してください。\n対象VMDファイル名に基づいて自動生成されますが、任意のパスに変更することも可能です。", \
                                                              is_aster=False, is_save=True, set_no=1)
        self.header_sizer.Add(self.output_smooth_vmd_file_ctrl.sizer, 1,
                              wx.EXPAND, 0)

        self.sizer.Add(self.header_sizer, 0, wx.EXPAND | wx.ALL, 5)

        self.target_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # ボーン名指定
        self.bone_target_txt_ctrl = wx.TextCtrl(
            self, wx.ID_ANY, "", wx.DefaultPosition, (450, 50),
            wx.HSCROLL | wx.VSCROLL | wx.TE_MULTILINE | wx.TE_READONLY)
        self.bone_target_txt_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.target_sizer.Add(self.bone_target_txt_ctrl, 1, wx.EXPAND | wx.ALL,
                              5)

        self.bone_target_btn_ctrl = wx.Button(self, wx.ID_ANY, u"対象指定",
                                              wx.DefaultPosition,
                                              wx.DefaultSize, 0)
        self.bone_target_btn_ctrl.SetToolTip(
            u"モーションに登録されているボーン・モーフから、スムージングにかけたいボーン・モーフを指定できます")
        self.bone_target_btn_ctrl.Bind(wx.EVT_BUTTON,
                                       self.on_click_bone_target)
        self.target_sizer.Add(self.bone_target_btn_ctrl, 0,
                              wx.ALIGN_BOTTOM | wx.ALL, 5)

        self.sizer.Add(self.target_sizer, 0, wx.EXPAND | wx.ALL, 5)

        self.setting_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 処理回数
        self.loop_cnt_txt = wx.StaticText(self, wx.ID_ANY, u"処理回数",
                                          wx.DefaultPosition, wx.DefaultSize,
                                          0)
        self.setting_sizer.Add(self.loop_cnt_txt, 0, wx.ALL, 5)

        self.loop_cnt_ctrl = wx.SpinCtrl(self,
                                         id=wx.ID_ANY,
                                         size=wx.Size(60, -1),
                                         value="2",
                                         min=1,
                                         max=99999999,
                                         initial=2)
        self.loop_cnt_ctrl.SetToolTip(
            u"スムージングを行う回数を指定してください。\n1回だと全打ちになります。\n2回目以降はフィルタをかけた上で間引きします。\n回数が増えると、変化が遅く、弱くなります。"
        )
        self.loop_cnt_ctrl.Bind(wx.EVT_SPINCTRL, self.on_change_file)
        self.setting_sizer.Add(self.loop_cnt_ctrl, 0, wx.ALL, 5)

        # 補間
        self.interpolation_txt = wx.StaticText(self, wx.ID_ANY, u"補間方法",
                                               wx.DefaultPosition,
                                               wx.DefaultSize, 0)
        self.setting_sizer.Add(self.interpolation_txt, 0, wx.ALL, 5)

        self.interpolation_ctrl = wx.Choice(
            self,
            id=wx.ID_ANY,
            choices=["補間曲線に従う", "補間曲線無視(円形)", "補間曲線無視(曲線)"])
        self.interpolation_ctrl.SetSelection(0)
        self.interpolation_ctrl.SetToolTip(u"キーとキーの補間方法を指定してください。\n「補間曲線に従う」は、補間曲線に従って繋ぎます。" \
                                           + "\n「補間曲線無視(円形)」は、補間曲線を無視して、\n3つのキーを円周上に置いた円になるように補間します。" \
                                           + "\n「補間曲線無視(曲線)」は、補間曲線を無視して、\nキーを滑らかな曲線(カトマル曲線)で繋いで補間します。")
        self.interpolation_ctrl.Bind(wx.EVT_CHOICE, self.on_change_file)
        self.setting_sizer.Add(self.interpolation_ctrl, 0, wx.ALL, 5)

        # 不要キー削除処理
        self.remove_unnecessary_flg_ctrl = wx.CheckBox(self, wx.ID_ANY,
                                                       u"不要キー削除処理を追加実行する",
                                                       wx.DefaultPosition,
                                                       wx.DefaultSize, 0)
        self.remove_unnecessary_flg_ctrl.SetToolTip(
            u"チェックを入れると、不要キー削除処理を追加で実行します。キーが減る分、キー間が少しズレる事があります。")
        self.remove_unnecessary_flg_ctrl.Bind(wx.EVT_CHECKBOX,
                                              self.on_change_file)
        self.setting_sizer.Add(self.remove_unnecessary_flg_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(self.setting_sizer, 0, wx.EXPAND | wx.ALL, 5)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # スムージング変換実行ボタン
        self.smooth_btn_ctrl = wx.Button(self, wx.ID_ANY,
                                         u"スムージング実行", wx.DefaultPosition,
                                         wx.Size(200, 50), 0)
        self.smooth_btn_ctrl.SetToolTip(u"VMDを滑らかに繋いだモーションを再生成します。")
        self.smooth_btn_ctrl.Bind(wx.EVT_BUTTON, self.on_convert_smooth)
        self.smooth_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        btn_sizer.Add(self.smooth_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.Layout()
        self.fit()

        # ボーン選択用ダイアログ
        self.bone_dialog = TargetBoneDialog(self.frame, self)
        self.bone_list = []

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_SMOOTH_THREAD, self.on_convert_smooth_result)

    # ファイル変更時の処理
    def on_change_file(self, event: wx.Event):
        self.set_output_vmd_path(event)

    def set_output_vmd_path(self, event, is_force=False):
        output_smooth_vmd_path = MFileUtils.get_output_smooth_vmd_path(
            self.smooth_vmd_file_ctrl.file_ctrl.GetPath(),
            self.smooth_model_file_ctrl.file_ctrl.GetPath(),
            self.output_smooth_vmd_file_ctrl.file_ctrl.GetPath(),
            self.interpolation_ctrl.GetSelection(),
            self.loop_cnt_ctrl.GetValue(), is_force)

        self.output_smooth_vmd_file_ctrl.file_ctrl.SetPath(
            output_smooth_vmd_path)

        if len(output_smooth_vmd_path) >= 255 and os.name == "nt":
            logger.error("生成予定のファイルパスがWindowsの制限を超えています。\n生成予定パス: {0}".format(
                output_smooth_vmd_path),
                         decoration=MLogger.DECORATION_BOX)

    # フォーム無効化
    def disable(self):
        self.smooth_vmd_file_ctrl.disable()
        self.smooth_model_file_ctrl.disable()
        self.output_smooth_vmd_file_ctrl.disable()
        self.loop_cnt_ctrl.Disable()
        self.interpolation_ctrl.Disable()
        self.smooth_btn_ctrl.Disable()
        self.remove_unnecessary_flg_ctrl.Disable()

    # フォーム無効化
    def enable(self):
        self.smooth_vmd_file_ctrl.enable()
        self.smooth_model_file_ctrl.enable()
        self.output_smooth_vmd_file_ctrl.enable()
        self.loop_cnt_ctrl.Enable()
        self.interpolation_ctrl.Enable()
        self.smooth_btn_ctrl.Enable()
        self.remove_unnecessary_flg_ctrl.Enable()

    def on_doubleclick(self, event: wx.Event):
        self.timer.Stop()
        logger.warning("ダブルクリックされました。", decoration=MLogger.DECORATION_BOX)
        event.Skip(False)
        return False

    # 多段分割変換
    def on_convert_smooth(self, event: wx.Event):
        self.timer = wx.Timer(self, TIMER_ID)
        self.timer.Start(200)
        self.Bind(wx.EVT_TIMER, self.on_convert, id=TIMER_ID)

    # スムージング変換
    def on_convert(self, event: wx.Event):
        self.timer.Stop()
        self.Unbind(wx.EVT_TIMER, id=TIMER_ID)
        # フォーム無効化
        self.disable()
        # タブ固定
        self.fix_tab()
        # コンソールクリア
        self.console_ctrl.Clear()
        # 出力先をスムージングパネルのコンソールに変更
        sys.stdout = self.console_ctrl

        wx.GetApp().Yield()

        self.smooth_vmd_file_ctrl.save()
        self.smooth_model_file_ctrl.save()

        # JSON出力
        MFileUtils.save_history(self.frame.mydir_path,
                                self.frame.file_hitories)

        self.elapsed_time = 0
        result = True
        result = self.smooth_vmd_file_ctrl.is_valid(
        ) and self.smooth_model_file_ctrl.is_valid() and result

        # スムージング変換開始
        if self.smooth_btn_ctrl.GetLabel(
        ) == "スムージング停止" and self.convert_smooth_worker:
            # フォーム無効化
            self.disable()
            # 停止状態でボタン押下時、停止
            self.convert_smooth_worker.stop()

            # タブ移動可
            self.frame.release_tab()
            # フォーム有効化
            self.frame.enable()
            # ワーカー終了
            self.convert_smooth_worker = None
            # プログレス非表示
            self.gauge_ctrl.SetValue(0)

            logger.warning("スムージングを中断します。", decoration=MLogger.DECORATION_BOX)
            self.smooth_btn_ctrl.SetLabel("スムージング実行")

            event.Skip(False)
        elif not self.convert_smooth_worker:
            # フォーム無効化
            self.disable()
            # タブ固定
            self.fix_tab()
            # コンソールクリア
            self.console_ctrl.Clear()
            # ラベル変更
            self.smooth_btn_ctrl.SetLabel("スムージング停止")
            self.smooth_btn_ctrl.Enable()

            self.convert_smooth_worker = SmoothWorkerThread(
                self.frame, SmoothThreadEvent, self.frame.is_saving,
                self.frame.is_out_log)
            self.convert_smooth_worker.start()

            event.Skip()
        else:
            logger.error("まだ処理が実行中です。終了してから再度実行してください。",
                         decoration=MLogger.DECORATION_BOX)
            event.Skip(False)

        return result

    # スムージング変換完了処理
    def on_convert_smooth_result(self, event: wx.Event):
        self.elapsed_time = event.elapsed_time
        logger.info("\n処理時間: %s", self.show_worked_time())
        self.smooth_btn_ctrl.SetLabel("スムージング実行")

        # 終了音
        self.frame.sound_finish()

        # タブ移動可
        self.release_tab()
        # フォーム有効化
        self.enable()
        # ワーカー終了
        self.convert_smooth_worker = None
        # プログレス非表示
        self.gauge_ctrl.SetValue(0)

    def show_worked_time(self):
        # 経過秒数を時分秒に変換
        td_m, td_s = divmod(self.elapsed_time, 60)

        if td_m == 0:
            worked_time = "{0:02d}秒".format(int(td_s))
        else:
            worked_time = "{0:02d}分{1:02d}秒".format(int(td_m), int(td_s))

        return worked_time

    def on_click_bone_target(self, event: wx.Event):
        self.disable()

        sys.stdout = self.console_ctrl
        # VMD読み込み
        self.smooth_vmd_file_ctrl.load()
        # PMX読み込み
        self.smooth_model_file_ctrl.load()

        if (self.smooth_vmd_file_ctrl.data and self.smooth_model_file_ctrl.data and \
                (self.smooth_vmd_file_ctrl.data.digest != self.bone_dialog.vmd_digest or self.smooth_model_file_ctrl.data.digest != self.bone_dialog.pmx_digest)):

            # データが揃ってたら押下可能
            self.bone_target_btn_ctrl.Enable()
            # リストクリア
            self.bone_target_txt_ctrl.SetValue("")
            # ボーン選択用ダイアログ
            self.bone_dialog.Destroy()
            self.bone_dialog = TargetBoneDialog(self.frame, self)
        else:
            if not self.smooth_vmd_file_ctrl.data or not self.smooth_model_file_ctrl.data:
                logger.error("対象モーションVMDもしくは適用モデルPMXが未指定です。",
                             decoration=MLogger.DECORATION_BOX)
                self.enable()
                return

        self.enable()

        if self.bone_dialog.ShowModal() == wx.ID_CANCEL:
            return  # the user changed their mind

        # 選択されたボーンリストを入力欄に設定
        self.bone_list = self.bone_dialog.get_bone_list()
        self.bone_target_txt_ctrl.SetValue(', '.join(self.bone_list))

        self.bone_dialog.Hide()

    def get_bone_list(self):
        return self.bone_list
Ejemplo n.º 15
0
class FilePanel(BasePanel):
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int,
                 file_hitories: dict):
        super().__init__(frame, parent, tab_idx)
        self.file_hitories = file_hitories
        self.timer = None

        # ファイルセット
        self.file_set = SizingFileSet(frame, self, self.file_hitories, 1)
        self.sizer.Add(self.file_set.set_sizer, 0, wx.ALL, 0)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 変換前チェックボタン
        self.check_btn_ctrl = wx.Button(self, wx.ID_ANY,
                                        u"変換前チェック", wx.DefaultPosition,
                                        wx.Size(200, 50), 0)
        self.check_btn_ctrl.SetToolTip(u"入力されたファイル情報で処理可能かどうか、チェックを行います。")
        self.check_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        self.check_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_check_click)
        btn_sizer.Add(self.check_btn_ctrl, 0, wx.ALL, 5)

        # 実行ボタン
        self.exec_btn_ctrl = wx.Button(self, wx.ID_ANY, u"VMDサイジング実行",
                                       wx.DefaultPosition, wx.Size(200, 50), 0)
        self.exec_btn_ctrl.SetToolTip(u"VMDサイジング処理を実行します。")
        self.exec_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        self.exec_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_exec_click)
        btn_sizer.Add(self.exec_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, -1), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.fit()

    # マルチプロセス用flush
    def print(self, txt):
        print(txt)
        wx.GetApp().Yield()

    # フォーム無効化
    def disable(self):
        self.file_set.disable()
        self.check_btn_ctrl.Disable()
        self.exec_btn_ctrl.Disable()

    # フォーム無効化
    def enable(self):
        self.file_set.enable()
        self.check_btn_ctrl.Enable()
        self.exec_btn_ctrl.Enable()

    def on_doubleclick(self, event: wx.Event):
        self.timer.Stop()
        logger.warning("ダブルクリックされました。", decoration=MLogger.DECORATION_BOX)
        event.Skip(False)
        return False

    def on_check_click(self, event: wx.Event):
        self.timer = wx.Timer(self, TIMER_ID)
        self.timer.Start(200)
        self.Bind(wx.EVT_TIMER, self.on_check, id=TIMER_ID)

    # 実行前チェック
    def on_check(self, event: wx.Event):
        # 出力先をファイルパネルのコンソールに変更
        sys.stdout = self.console_ctrl

        if self.check_btn_ctrl.GetLabel(
        ) == "読み込み処理停止" and self.frame.load_worker:
            # フォーム無効化
            self.disable()
            # 停止状態でボタン押下時、停止
            self.frame.load_worker.stop()

            # タブ移動可
            self.frame.release_tab()
            # フォーム有効化
            self.frame.enable()
            # ワーカー終了
            self.frame.load_worker = None
            # プログレス非表示
            self.gauge_ctrl.SetValue(0)

            self.timer.Stop()

            logger.warning("読み込み処理を中断します。", decoration=MLogger.DECORATION_BOX)

            event.Skip(False)
        elif not self.frame.load_worker:
            # フォーム無効化
            self.disable()
            # タブ固定
            self.fix_tab()
            # コンソールクリア
            self.console_ctrl.Clear()

            # 履歴保持
            self.save()

            self.timer.Stop()

            # 一旦読み込み(そのままチェック)
            self.frame.load(event, target_idx=0)

            event.Skip()
        else:
            self.timer.Stop()

            logger.error("まだ処理が実行中です。終了してから再度実行してください。",
                         decoration=MLogger.DECORATION_BOX)
            event.Skip(False)

    def on_exec_click(self, event: wx.Event):
        self.timer = wx.Timer(self, TIMER_ID)
        self.timer.Start(200)
        self.Bind(wx.EVT_TIMER, self.on_exec, id=TIMER_ID)

    # サイジング実行
    def on_exec(self, event: wx.Event):
        # 出力先をファイルパネルのコンソールに変更
        sys.stdout = self.console_ctrl

        if self.exec_btn_ctrl.GetLabel() == "VMDサイジング停止" and self.frame.worker:
            # フォーム無効化
            self.disable()
            # 停止状態でボタン押下時、停止
            self.frame.worker.stop()

            # タブ移動可
            self.frame.release_tab()
            # フォーム有効化
            self.frame.enable()
            # ワーカー終了
            self.frame.worker = None
            # プログレス非表示
            self.gauge_ctrl.SetValue(0)

            self.timer.Stop()

            logger.warning("VMDサイジングを中断します。",
                           decoration=MLogger.DECORATION_BOX)

            event.Skip(False)
        elif not self.frame.worker:
            # フォーム無効化
            self.disable()
            # タブ固定
            self.fix_tab()
            # コンソールクリア
            self.console_ctrl.Clear()

            # 履歴保持
            self.save()

            self.timer.Stop()

            # サイジング可否チェックの後に実行
            self.frame.load(event, is_exec=True, target_idx=0)

            event.Skip()
        else:
            self.timer.Stop()

            logger.error("まだ処理が実行中です。終了してから再度実行してください。",
                         decoration=MLogger.DECORATION_BOX)
            event.Skip(False)

    def set_output_vmd_path(self, event, is_force=False):
        self.file_set.set_output_vmd_path(event, is_force)
        # カメラ出力パスも一緒に変更する
        self.frame.camera_panel_ctrl.header_panel.set_output_vmd_path(event)

    def save(self):

        # 履歴保持
        self.frame.file_panel_ctrl.file_set.save()

        # multiのも全部保持
        for file_set in self.frame.multi_panel_ctrl.file_set_list:
            file_set.save()

        # カメラ履歴保持
        self.frame.camera_panel_ctrl.save()

        # カメラ元モデル保持
        for camera_set in self.frame.camera_panel_ctrl.camera_set_dict.values(
        ):
            camera_set.camera_model_file_ctrl.save()

        # JSON出力
        MFileUtils.save_history(self.frame.mydir_path,
                                self.frame.file_hitories)
Ejemplo n.º 16
0
class BulkPanel(BasePanel):
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int):
        super().__init__(frame, parent, tab_idx)

        self.description_txt = wx.StaticText(self, wx.ID_ANY,
                                             "設定を一括で指定して、連続して処理させる事ができます。",
                                             wx.DefaultPosition,
                                             wx.DefaultSize, 0)
        self.sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                         wx.DefaultSize, wx.LI_HORIZONTAL)
        self.sizer.Add(self.static_line, 0, wx.EXPAND | wx.ALL, 5)

        # バルクBULKファイルコントロール
        self.bulk_csv_file_ctrl = HistoryFilePickerCtrl(frame, self, u"一括処理用CSV", u"一括処理用CSVファイルを開く", ("csv"), wx.FLP_DEFAULT_STYLE, \
                                                        u"一括処理用のCSVを指定してください。\nフォーマットは、DLボタンから取得できます。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                        file_model_spacer=0, title_parts_ctrl=None, title_parts2_ctrl=None, \
                                                        file_histories_key="bulk_csv", is_change_output=False, is_aster=False, is_save=False, set_no=0)
        self.sizer.Add(self.bulk_csv_file_ctrl.sizer, 0, wx.EXPAND | wx.ALL, 0)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 一括サイジング確認ボタン
        self.check_btn_ctrl = wx.Button(self, wx.ID_ANY,
                                        u"一括サイジング確認", wx.DefaultPosition,
                                        wx.Size(200, 50), 0)
        self.check_btn_ctrl.SetToolTip(u"指定されたCSVデータの設定を確認します。")
        self.check_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        self.check_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_check_click)
        btn_sizer.Add(self.check_btn_ctrl, 0, wx.ALL, 5)

        # 一括サイジング実行ボタン
        self.bulk_btn_ctrl = wx.Button(self, wx.ID_ANY, u"一括サイジング実行",
                                       wx.DefaultPosition, wx.Size(200, 50), 0)
        self.bulk_btn_ctrl.SetToolTip(u"一括でサイジングを実行します")
        self.bulk_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        self.bulk_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_bulk_click)
        btn_sizer.Add(self.bulk_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.fit()

        # 変換完了処理バインド
        self.frame.Bind(EVT_BULK_LOAD_THREAD, self.on_load_result)
        self.frame.Bind(EVT_BULK_SIZING_THREAD, self.on_exec_result)

    # フォーム無効化
    def disable(self):
        self.bulk_csv_file_ctrl.disable()
        self.bulk_btn_ctrl.Disable()
        self.check_btn_ctrl.Disable()

    # フォーム無効化
    def enable(self):
        self.bulk_csv_file_ctrl.enable()
        self.bulk_btn_ctrl.Enable()
        self.check_btn_ctrl.Enable()

    def on_doubleclick(self, event: wx.Event):
        self.timer.Stop()
        logger.warning("ダブルクリックされました。", decoration=MLogger.DECORATION_BOX)
        event.Skip(False)
        return False

    def on_bulk_click(self, event: wx.Event):
        self.timer = wx.Timer(self, TIMER_ID)
        self.timer.Start(200)
        self.Bind(wx.EVT_TIMER, self.on_bulk, id=TIMER_ID)

    # サイジング一括実行
    def on_bulk(self, event: wx.Event):
        if self.timer:
            self.timer.Stop()
            self.Unbind(wx.EVT_TIMER, id=TIMER_ID)

        # 出力先をファイルパネルのコンソールに変更
        sys.stdout = self.console_ctrl

        if self.bulk_btn_ctrl.GetLabel() == "一括サイジング停止" and self.frame.worker:
            # フォーム無効化
            self.disable()
            # 停止状態でボタン押下時、停止
            self.frame.worker.stop()

            # タブ移動可
            self.release_tab()
            # フォーム有効化
            self.enable()
            # ワーカー終了
            self.frame.worker = None
            # プログレス非表示
            self.gauge_ctrl.SetValue(0)

            logger.warning("VMDサイジング一括処理を中断します。",
                           decoration=MLogger.DECORATION_BOX)

            event.Skip(False)
        elif not self.frame.worker:
            # フォーム無効化
            self.disable()
            # タブ固定
            self.fix_tab()
            # コンソールクリア
            self.console_ctrl.Clear()

            # 履歴保持
            self.save()

            # サイジング可否チェックの後に実行
            self.check(event, True)

            event.Skip()
        else:
            logger.error("まだ処理が実行中です。終了してから再度実行してください。",
                         decoration=MLogger.DECORATION_BOX)
            event.Skip(False)

    def on_check_click(self, event: wx.Event):
        self.timer = wx.Timer(self, TIMER_ID)
        self.timer.Start(200)
        self.Bind(wx.EVT_TIMER, self.on_check, id=TIMER_ID)

    # サイジング一括確認
    def on_check(self, event: wx.Event):
        if self.timer:
            self.timer.Stop()
            self.Unbind(wx.EVT_TIMER, id=TIMER_ID)

        # 出力先をファイルパネルのコンソールに変更
        sys.stdout = self.console_ctrl

        # サイジング可否チェックのみ
        self.check(event, False)
        return

    def save(self):
        # 履歴保持
        self.bulk_csv_file_ctrl.save()

        # JSON出力
        MFileUtils.save_history(self.frame.mydir_path,
                                self.frame.file_hitories)

    # データチェック
    def check(self, event: wx.Event, is_exec: bool):
        # フォーム無効化
        self.disable()
        # タブ固定
        self.fix_tab()

        if not self.bulk_csv_file_ctrl.is_valid():
            # CSVパスが無効な場合、終了
            self.enable()
            self.release_tab()
            return

        result = True
        with open(self.bulk_csv_file_ctrl.path(), encoding='cp932',
                  mode='r') as f:
            reader = csv.reader(f)
            next(reader)  # ヘッダーを読み飛ばす

            prev_group_no = -1
            now_model_no = -1
            service_data_txt = ""
            for ridx, rows in enumerate(reader):
                row_no = ridx
                group_no_result, group_no = self.read_csv_row(
                    rows, row_no, 0, "グループNo", True, int, r"\d+", "数値のみ", None)
                org_motion_result, org_motion_path = self.read_csv_row(
                    rows, row_no, 1, "調整対象モーションVMD/VPD", True, str, None, None,
                    (".vmd", ".vpd"))
                org_model_result, org_model_path = self.read_csv_row(
                    rows, row_no, 2, "モーション作成元モデルPMX", True, str, None, None,
                    (".pmx"))
                rep_model_result, rep_model_path = self.read_csv_row(
                    rows, row_no, 3, "モーション変換先モデルPMX", True, str, None, None,
                    (".pmx"))
                stance_center_xz_result, stance_center_xz_datas = self.read_csv_row(
                    rows, row_no, 4, "センターXZ補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                stance_upper_result, stance_upper_datas = self.read_csv_row(
                    rows, row_no, 5, "上半身補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                stance_lower_result, stance_lower_datas = self.read_csv_row(
                    rows, row_no, 6, "下半身補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                stance_leg_ik_result, stance_leg_ik_datas = self.read_csv_row(
                    rows, row_no, 7, "足IK補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                stance_toe_result, stance_toe_datas = self.read_csv_row(
                    rows, row_no, 8, "つま先補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                stance_toe_ik_result, stance_toe_ik_datas = self.read_csv_row(
                    rows, row_no, 9, "つま先IK補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                stance_shoulder_result, stance_shoulder_datas = self.read_csv_row(
                    rows, row_no, 10, "肩補正", True, int, r"^(0|1)$", "0 もしくは 1",
                    None)
                stance_center_y_result, stance_center_y_datas = self.read_csv_row(
                    rows, row_no, 11, "センターY補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                separate_twist_result, separate_twist_datas = self.read_csv_row(
                    rows, row_no, 12, "捩り分散", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                morph_result, morph_datas = self.read_csv_row(
                    rows, row_no, 13, "モーフ置換", False, str,
                    r"[^\:]+\:[^\:]+\:\d+\.?\d*\;", "元:先:大きさ;", None)
                arm_avoidance_result, arm_avoidance_datas = self.read_csv_row(
                    rows, row_no, 14, "接触回避", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                avoidance_name_result, avoidance_name_datas = self.read_csv_row(
                    rows, row_no, 15, "接触回避剛体", False, str, r"[^\;]+\;",
                    "剛体名;", None)
                arm_alignment_result, arm_alignment_datas = self.read_csv_row(
                    rows, row_no, 16, "位置合わせ", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                finger_alignment_result, finger_alignment_datas = self.read_csv_row(
                    rows, row_no, 17, "指位置合わせ", False, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                floor_alignment_result, floor_alignment_datas = self.read_csv_row(
                    rows, row_no, 18, "床位置合わせ", False, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                arm_alignment_length_result, arm_alignment_length_datas = self.read_csv_row(
                    rows, row_no, 19, "手首の距離", False, float, None, None, None)
                finger_alignment_length_result, finger_alignment_length_datas = self.read_csv_row(
                    rows, row_no, 20, "指の距離", False, float, None, None, None)
                floor_alignment_length_result, floor_alignment_length_datas = self.read_csv_row(
                    rows, row_no, 21, "床との距離", False, float, None, None, None)
                arm_check_skip_result, arm_check_skip_datas = self.read_csv_row(
                    rows, row_no, 22, "腕チェックスキップ", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                org_camera_motion_result, org_camera_motion_path = self.read_csv_row(
                    rows, row_no, 23, "カメラモーションVMD", False, str, None, None,
                    (".vmd"))
                camera_length_result, camera_length_datas = self.read_csv_row(
                    rows, row_no, 24, "距離稼働範囲", False, float,
                    r"^[1-9]\d*\.?\d*", "1以上", None)
                org_camera_model_result, org_camera_model_path = self.read_csv_row(
                    rows, row_no, 25, "カメラ作成元モデルPMX", False, str, None, None,
                    (".pmx"))
                camera_y_offset_result, camera_y_offset_datas = self.read_csv_row(
                    rows, row_no, 26, "全長Yオフセット", False, float, None, None,
                    None)

                result = result & group_no_result & org_motion_result & org_model_result & rep_model_result & stance_center_xz_result \
                    & stance_upper_result & stance_lower_result & stance_leg_ik_result & stance_toe_result & stance_toe_ik_result & stance_shoulder_result \
                    & stance_center_y_result & separate_twist_result & arm_check_skip_result & morph_result & arm_avoidance_result & avoidance_name_result \
                    & arm_alignment_result & finger_alignment_result & floor_alignment_result & arm_alignment_length_result & finger_alignment_length_result \
                    & floor_alignment_length_result & org_camera_motion_result & camera_length_result & org_camera_model_result \
                    & camera_y_offset_result

                if result:
                    if prev_group_no != group_no[0]:
                        now_model_no = 1

                        if len(service_data_txt) > 0:
                            # 既存データがある場合、出力
                            logger.info(service_data_txt,
                                        decoration=MLogger.DECORATION_BOX)

                        # 先頭モーションの場合
                        service_data_txt = f"\n【グループNo.{group_no[0]}】 \n"

                        arm_avoidance_txt = "あり" if arm_avoidance_datas[
                            0] == 1 else "なし"
                        service_data_txt = f"{service_data_txt} 剛体接触回避: {arm_avoidance_txt}\n"
                        arm_alignment_txt = "あり" if arm_alignment_datas[
                            0] == 1 else "なし"
                        service_data_txt = f"{service_data_txt} 手首位置合わせ: {arm_alignment_txt} ({arm_alignment_length_datas})\n"
                        finger_alignment_txt = "あり" if finger_alignment_datas[
                            0] == 1 else "なし"
                        service_data_txt = f"{service_data_txt} 指位置合わせ: {finger_alignment_txt} ({finger_alignment_length_datas})\n"
                        floor_alignment_txt = "あり" if floor_alignment_datas[
                            0] == 1 else "なし"
                        service_data_txt = f"{service_data_txt} 床位置合わせ: {floor_alignment_txt} ({floor_alignment_length_datas})\n"
                        arm_check_skip_txt = "あり" if arm_check_skip_datas[
                            0] == 1 else "なし"
                        service_data_txt = f"{service_data_txt} 腕チェックスキップ: {arm_check_skip_txt}\n"

                        service_data_txt = f"{service_data_txt} カメラ: {org_camera_motion_path}\n"
                        service_data_txt = f"{service_data_txt} 距離制限: {camera_length_datas}\n"
                    else:
                        # 複数人モーションの場合、No加算
                        now_model_no += 1

                    service_data_txt = f"{service_data_txt}\n 【人物No.{now_model_no}】 --------- \n"

                    service_data_txt = f"{service_data_txt}  モーション: {org_motion_path}\n"
                    service_data_txt = f"{service_data_txt}  作成元モデル: {org_model_path}\n"
                    service_data_txt = f"{service_data_txt}  変換先モデル: {rep_model_path}\n"
                    service_data_txt = f"{service_data_txt}  カメラ作成元モデル: {org_camera_model_path}\n"
                    service_data_txt = f"{service_data_txt}  Yオフセット: {camera_y_offset_datas}\n"

                    detail_stance_list = []
                    if stance_center_xz_datas[0] == 1:
                        detail_stance_list.append("センターXZ補正")
                    if stance_upper_datas[0] == 1:
                        detail_stance_list.append("上半身補正")
                    if stance_lower_datas[0] == 1:
                        detail_stance_list.append("下半身補正")
                    if stance_leg_ik_datas[0] == 1:
                        detail_stance_list.append("足IK補正")
                    if stance_toe_datas[0] == 1:
                        detail_stance_list.append("つま先補正")
                    if stance_toe_ik_datas[0] == 1:
                        detail_stance_list.append("つま先IK補正")
                    if stance_shoulder_datas[0] == 1:
                        detail_stance_list.append("肩補正")
                    if stance_center_y_datas[0] == 1:
                        detail_stance_list.append("センターY補正")
                    detail_stance_txt = ", ".join(detail_stance_list)

                    service_data_txt = f"{service_data_txt}  スタンス追加補正有無: {detail_stance_txt}\n"

                    twist_txt = "あり" if separate_twist_datas[0] == 1 else "なし"
                    service_data_txt = f"{service_data_txt}  捩り分散有無: {twist_txt}\n"

                    # モーフデータ
                    morph_list = []
                    for morph_data in morph_datas:
                        m = re.findall(r"([^\:]+)\:([^\:]+)\:(\d+\.?\d*)\;",
                                       morph_data)
                        morph_list.append(
                            f"{m[0][0]} → {m[0][1]} ({float(m[0][2])})")
                    morph_txt = ", ".join(morph_list)
                    service_data_txt = f"{service_data_txt}  モーフ置換: {morph_txt}\n"

                    # 接触回避データ
                    arm_avoidance_name_list = []
                    for avoidance_data in avoidance_name_datas:
                        m = re.findall(r"([^\:]+)\;", avoidance_data)
                        arm_avoidance_name_list.append(m[0])
                    arm_avoidance_name_txt = ", ".join(arm_avoidance_name_list)
                    service_data_txt = f"{service_data_txt}  対象剛体名: {arm_avoidance_name_txt}\n"

                prev_group_no = group_no[0]

        if result:
            if is_exec:
                # 全部OKなら処理開始
                self.load(event, 0)
            else:

                if len(service_data_txt) > 0:
                    # 既存データがある場合、最後に出力
                    logger.info(service_data_txt,
                                decoration=MLogger.DECORATION_BOX)

                # OKかつ確認のみの場合、出力して終了
                logger.info("CSVデータの確認が成功しました。",
                            decoration=MLogger.DECORATION_BOX,
                            title="OK")

                self.enable()
                self.release_tab()
                return
        else:
            logger.error("CSVデータに不整合があるため、処理を中断します",
                         decoration=MLogger.DECORATION_BOX)

            self.enable()
            self.release_tab()

            return

    def read_csv_row(self, rows: list, row_no: int, row_idx: int,
                     row_name: str, row_required: bool, row_type: type,
                     row_regex: str, row_regex_str: str, path_exts: tuple):
        try:
            if row_required and (len(rows) < row_idx or not rows[row_idx]):
                logger.warning("%s行目の%s(%s列目)が設定されていません", row_no + 1, row_name,
                               row_idx + 1)
                return False, None

            try:
                if rows[row_idx] and not row_type(rows[row_idx]):
                    pass
            except Exception:
                row_type_str = "半角整数" if row_type == int else "半角数字"
                logger.warning("%s行目の%s(%s列目)の型(%s)が合っていません", row_no + 1,
                               row_name, row_idx + 1, row_type_str)
                return False, None

            if rows[row_idx] and row_regex and not re.findall(
                    row_regex, rows[row_idx]):
                logger.warning("%s行目の%s(%s列目)の表示形式(%s)が合っていません", row_no + 1,
                               row_name, row_idx + 1, row_regex_str)
                return False, None

            if rows[row_idx] and path_exts:
                if not rows[row_idx] or (not os.path.exists(rows[row_idx])
                                         or not os.path.isfile(rows[row_idx])):
                    logger.warning("%s行目の%s(%s列目)のファイルが存在していません", row_no + 1,
                                   row_name, row_idx + 1)
                    return False, None

                # ファイル名・拡張子
                file_name, ext = os.path.splitext(
                    os.path.basename(rows[row_idx]))
                if (ext not in path_exts):
                    logger.warning("%s行目の%s(%s列目)のファイル拡張子(%s)が合っていません", row_no + 1, row_name, row_idx + 1, \
                                   ','.join(map(str, path_exts)) if len(path_exts) > 1 else path_exts)
                    return False, None

            # 読み取り実施
            if rows[row_idx] and row_regex:
                # 正規表現の場合は、リスト変換して返す
                if row_type:
                    # 型指定がある場合は変換して返す
                    return True, [
                        row_type(v)
                        for v in re.findall(row_regex, rows[row_idx])
                    ]
                else:
                    return True, re.findall(row_regex, rows[row_idx])

            if (row_type == float or row_type == int) and not rows[row_idx]:
                # 数値で任意はゼロ設定
                return True, 0
            elif row_type:
                return True, row_type(rows[row_idx])

            return True, rows[row_idx]
        except Exception as e:
            logger.warning("%s行目の%s(%s列目)の読み取りに失敗しました\n%s", row_no + 1,
                           row_name, row_idx + 1, e)
            return False, None

    # 読み込み
    def load(self, event, line_idx):
        # グループ単位で設定
        now_group_no = -1
        now_motion_idx = -1
        row_no = 0
        with open(self.bulk_csv_file_ctrl.path(), encoding='cp932',
                  mode='r') as f:
            reader = csv.reader(f)
            next(reader)  # ヘッダーを読み飛ばす

            for ridx, rows in enumerate(reader):
                row_no = ridx

                if row_no < line_idx:
                    # 自分より前の行の場合、スキップ
                    continue

                group_no_result, group_no = self.read_csv_row(
                    rows, row_no, 0, "グループNo", True, int, r"\d+", "数値のみ", None)

                if len(group_no) == 0:
                    # グループNOが取れなかったから終了
                    return

                if len(group_no) > 0 and row_no == line_idx:
                    # 指定INDEXに到達したら設定して読み取り開始
                    now_motion_idx = 0
                    now_group_no = group_no[0]
                else:
                    now_motion_idx += 1

                if len(group_no) > 0 and group_no[0] != now_group_no:
                    # グループNOが変わっていたら、そのまま終了
                    break

                group_no_result, group_no = self.read_csv_row(
                    rows, row_no, 0, "グループNo", True, int, r"\d+", "数値のみ", None)
                org_motion_result, org_motion_path = self.read_csv_row(
                    rows, row_no, 1, "調整対象モーションVMD/VPD", True, str, None, None,
                    (".vmd", ".vpd"))
                org_model_result, org_model_path = self.read_csv_row(
                    rows, row_no, 2, "モーション作成元モデルPMX", True, str, None, None,
                    (".pmx"))
                rep_model_result, rep_model_path = self.read_csv_row(
                    rows, row_no, 3, "モーション変換先モデルPMX", True, str, None, None,
                    (".pmx"))
                stance_center_xz_result, stance_center_xz_datas = self.read_csv_row(
                    rows, row_no, 4, "センターXZ補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                stance_upper_result, stance_upper_datas = self.read_csv_row(
                    rows, row_no, 5, "上半身補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                stance_lower_result, stance_lower_datas = self.read_csv_row(
                    rows, row_no, 6, "下半身補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                stance_leg_ik_result, stance_leg_ik_datas = self.read_csv_row(
                    rows, row_no, 7, "足IK補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                stance_toe_result, stance_toe_datas = self.read_csv_row(
                    rows, row_no, 8, "つま先補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                stance_toe_ik_result, stance_toe_ik_datas = self.read_csv_row(
                    rows, row_no, 9, "つま先IK補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                stance_shoulder_result, stance_shoulder_datas = self.read_csv_row(
                    rows, row_no, 10, "肩補正", True, int, r"^(0|1)$", "0 もしくは 1",
                    None)
                stance_center_y_result, stance_center_y_datas = self.read_csv_row(
                    rows, row_no, 11, "センターY補正", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                separate_twist_result, separate_twist_datas = self.read_csv_row(
                    rows, row_no, 12, "捩り分散", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                morph_result, morph_datas = self.read_csv_row(
                    rows, row_no, 13, "モーフ置換", False, str,
                    r"[^\:]+\:[^\:]+\:\d+\.?\d*\;", "元:先:大きさ;", None)
                arm_avoidance_result, arm_avoidance_datas = self.read_csv_row(
                    rows, row_no, 14, "接触回避", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                avoidance_name_result, avoidance_name_datas = self.read_csv_row(
                    rows, row_no, 15, "接触回避剛体", False, str, r"[^\;]+\;",
                    "剛体名;", None)
                arm_alignment_result, arm_alignment_datas = self.read_csv_row(
                    rows, row_no, 16, "位置合わせ", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                finger_alignment_result, finger_alignment_datas = self.read_csv_row(
                    rows, row_no, 17, "指位置合わせ", False, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                floor_alignment_result, floor_alignment_datas = self.read_csv_row(
                    rows, row_no, 18, "床位置合わせ", False, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                arm_alignment_length_result, arm_alignment_length_datas = self.read_csv_row(
                    rows, row_no, 19, "手首の距離", False, float, None, None, None)
                finger_alignment_length_result, finger_alignment_length_datas = self.read_csv_row(
                    rows, row_no, 20, "指の距離", False, float, None, None, None)
                floor_alignment_length_result, floor_alignment_length_datas = self.read_csv_row(
                    rows, row_no, 21, "床との距離", False, float, None, None, None)
                arm_check_skip_result, arm_check_skip_datas = self.read_csv_row(
                    rows, row_no, 22, "腕チェックスキップ", True, int, r"^(0|1)$",
                    "0 もしくは 1", None)
                org_camera_motion_result, org_camera_motion_path = self.read_csv_row(
                    rows, row_no, 23, "カメラモーションVMD", False, str, None, None,
                    (".vmd"))
                camera_length_result, camera_length_datas = self.read_csv_row(
                    rows, row_no, 24, "距離稼働範囲", False, float, None, None, None)
                org_camera_model_result, org_camera_model_path = self.read_csv_row(
                    rows, row_no, 25, "カメラ作成元モデルPMX", False, str, None, None,
                    (".pmx"))
                camera_y_offset_result, camera_y_offset_datas = self.read_csv_row(
                    rows, row_no, 26, "全長Yオフセット", False, float, None, None,
                    None)

                if now_motion_idx == 0:
                    # 複数パネルはクリア
                    self.frame.multi_panel_ctrl.on_clear_set(event)

                    # ファイルパネル設定
                    self.frame.file_panel_ctrl.file_set.motion_vmd_file_ctrl.file_ctrl.SetPath(
                        org_motion_path)
                    self.frame.file_panel_ctrl.file_set.org_model_file_ctrl.file_ctrl.SetPath(
                        org_model_path)
                    self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.file_ctrl.SetPath(
                        rep_model_path)
                    self.frame.file_panel_ctrl.file_set.output_vmd_file_ctrl.file_ctrl.SetPath(
                        "")

                    self.frame.file_panel_ctrl.file_set.org_model_file_ctrl.title_parts_ctrl.SetValue(
                        stance_center_xz_datas[0] | stance_upper_datas[0] | stance_lower_datas[0] | stance_leg_ik_datas[0] | \
                        stance_toe_datas[0] | stance_toe_ik_datas[0] | stance_shoulder_datas[0] | stance_center_y_datas[0]
                    )

                    # スタンス追加補正
                    self.frame.file_panel_ctrl.file_set.selected_stance_details = []
                    if stance_center_xz_datas[0] == 1:
                        self.frame.file_panel_ctrl.file_set.selected_stance_details.append(
                            0)
                    if stance_upper_datas[0] == 1:
                        self.frame.file_panel_ctrl.file_set.selected_stance_details.append(
                            1)
                    if stance_lower_datas[0] == 1:
                        self.frame.file_panel_ctrl.file_set.selected_stance_details.append(
                            2)
                    if stance_leg_ik_datas[0] == 1:
                        self.frame.file_panel_ctrl.file_set.selected_stance_details.append(
                            3)
                    if stance_toe_datas[0] == 1:
                        self.frame.file_panel_ctrl.file_set.selected_stance_details.append(
                            4)
                    if stance_toe_ik_datas[0] == 1:
                        self.frame.file_panel_ctrl.file_set.selected_stance_details.append(
                            5)
                    if stance_shoulder_datas[0] == 1:
                        self.frame.file_panel_ctrl.file_set.selected_stance_details.append(
                            6)
                    if stance_center_y_datas[0] == 1:
                        self.frame.file_panel_ctrl.file_set.selected_stance_details.append(
                            7)

                    # 捩り分散
                    self.frame.file_panel_ctrl.file_set.rep_model_file_ctrl.title_parts_ctrl.SetValue(
                        separate_twist_datas[0])

                    # 腕チェックスキップ
                    self.frame.arm_panel_ctrl.arm_check_skip_flg_ctrl.SetValue(
                        arm_check_skip_datas[0])

                    # モーフデータ
                    self.frame.morph_panel_ctrl.bulk_morph_set_dict[1] = []
                    for morph_data in morph_datas:
                        m = re.findall(r"([^\:]+)\:([^\:]+)\:(\d+\.?\d*)\;",
                                       morph_data)
                        self.frame.morph_panel_ctrl.bulk_morph_set_dict[
                            1].append((m[0][0], m[0][1], float(m[0][2])))

                    # 接触回避
                    self.frame.arm_panel_ctrl.arm_process_flg_avoidance.SetValue(
                        arm_avoidance_datas[0])

                    # 接触回避データ
                    self.frame.arm_panel_ctrl.bulk_avoidance_set_dict[0] = []
                    for avoidance_data in avoidance_name_datas:
                        m = re.findall(r"([^\:]+)\;", avoidance_data)
                        self.frame.arm_panel_ctrl.bulk_avoidance_set_dict[
                            0].append(m[0][0])

                    # 位置合わせ
                    self.frame.arm_panel_ctrl.arm_process_flg_alignment.SetValue(
                        arm_alignment_datas[0])
                    self.frame.arm_panel_ctrl.arm_alignment_finger_flg_ctrl.SetValue(
                        finger_alignment_datas[0])
                    self.frame.arm_panel_ctrl.arm_alignment_floor_flg_ctrl.SetValue(
                        floor_alignment_datas[0])

                    # 位置合わせ距離
                    self.frame.arm_panel_ctrl.alignment_distance_wrist_slider.SetValue(
                        arm_alignment_length_datas)
                    self.frame.arm_panel_ctrl.alignment_distance_finger_slider.SetValue(
                        finger_alignment_length_datas)
                    self.frame.arm_panel_ctrl.alignment_distance_floor_slider.SetValue(
                        floor_alignment_length_datas)

                    # カメラ
                    self.frame.camera_panel_ctrl.camera_vmd_file_ctrl.file_ctrl.SetPath(
                        org_camera_motion_path)
                    self.frame.camera_panel_ctrl.output_camera_vmd_file_ctrl.file_ctrl.SetPath(
                        "")
                    self.frame.camera_panel_ctrl.camera_length_slider.SetValue(
                        camera_length_datas)

                    # カメラ元情報
                    self.frame.camera_panel_ctrl.initialize(event)
                    self.frame.camera_panel_ctrl.camera_set_dict[
                        1].camera_model_file_ctrl.file_ctrl.SetPath(
                            org_camera_model_path)
                    self.frame.camera_panel_ctrl.camera_set_dict[
                        1].camera_offset_y_ctrl.SetValue(camera_y_offset_datas)

                    # 出力パス変更
                    self.frame.file_panel_ctrl.file_set.set_output_vmd_path(
                        event)
                    self.frame.camera_panel_ctrl.set_output_vmd_path(event)
                else:
                    # 複数パネルセット追加
                    self.frame.multi_panel_ctrl.on_add_set(event)

                    # ファイルパネル設定
                    self.frame.multi_panel_ctrl.file_set_list[
                        now_motion_idx -
                        1].motion_vmd_file_ctrl.file_ctrl.SetPath(
                            org_motion_path)
                    self.frame.multi_panel_ctrl.file_set_list[
                        now_motion_idx -
                        1].org_model_file_ctrl.file_ctrl.SetPath(
                            org_model_path)
                    self.frame.multi_panel_ctrl.file_set_list[
                        now_motion_idx -
                        1].rep_model_file_ctrl.file_ctrl.SetPath(
                            rep_model_path)
                    self.frame.multi_panel_ctrl.file_set_list[
                        now_motion_idx -
                        1].output_vmd_file_ctrl.file_ctrl.SetPath("")

                    self.frame.multi_panel_ctrl.file_set_list[now_motion_idx - 1].org_model_file_ctrl.title_parts_ctrl.SetValue(
                        stance_center_xz_datas[0] | stance_upper_datas[0] | stance_lower_datas[0] | stance_leg_ik_datas[0] | \
                        stance_toe_datas[0] | stance_toe_ik_datas[0] | stance_shoulder_datas[0] | stance_center_y_datas[0]
                    )

                    # スタンス追加補正
                    self.frame.multi_panel_ctrl.file_set_list[
                        now_motion_idx - 1].selected_stance_details = []
                    if stance_center_xz_datas[0] == 1:
                        self.frame.multi_panel_ctrl.file_set_list[
                            now_motion_idx -
                            1].selected_stance_details.append(0)
                    if stance_upper_datas[0] == 1:
                        self.frame.multi_panel_ctrl.file_set_list[
                            now_motion_idx -
                            1].selected_stance_details.append(1)
                    if stance_lower_datas[0] == 1:
                        self.frame.multi_panel_ctrl.file_set_list[
                            now_motion_idx -
                            1].selected_stance_details.append(2)
                    if stance_leg_ik_datas[0] == 1:
                        self.frame.multi_panel_ctrl.file_set_list[
                            now_motion_idx -
                            1].selected_stance_details.append(3)
                    if stance_toe_datas[0] == 1:
                        self.frame.multi_panel_ctrl.file_set_list[
                            now_motion_idx -
                            1].selected_stance_details.append(4)
                    if stance_toe_ik_datas[0] == 1:
                        self.frame.multi_panel_ctrl.file_set_list[
                            now_motion_idx -
                            1].selected_stance_details.append(5)
                    if stance_shoulder_datas[0] == 1:
                        self.frame.multi_panel_ctrl.file_set_list[
                            now_motion_idx -
                            1].selected_stance_details.append(6)
                    if stance_center_y_datas[0] == 1:
                        self.frame.multi_panel_ctrl.file_set_list[
                            now_motion_idx -
                            1].selected_stance_details.append(7)

                    # 捩り分散
                    self.frame.multi_panel_ctrl.file_set_list[
                        now_motion_idx -
                        1].rep_model_file_ctrl.title_parts_ctrl.SetValue(
                            separate_twist_datas[0])

                    # モーフデータ
                    self.frame.morph_panel_ctrl.bulk_morph_set_dict[
                        now_motion_idx + 1] = []
                    for morph_data in morph_datas:
                        m = re.findall(r"([^\:]+)\:([^\:]+)\:(\d+\.?\d*)\;",
                                       morph_data)
                        self.frame.morph_panel_ctrl.bulk_morph_set_dict[
                            now_motion_idx + 1].append(
                                (m[0][0], m[0][1], float(m[0][2])))

                    # 接触回避データ
                    self.frame.arm_panel_ctrl.bulk_avoidance_set_dict[
                        now_motion_idx - 1] = []
                    for avoidance_data in avoidance_name_datas:
                        m = re.findall(r"([^\:]+)\;", avoidance_data)
                        self.frame.arm_panel_ctrl.bulk_avoidance_set_dict[
                            now_motion_idx - 1].append(m[0][0])

                    # 指位置合わせは常に0(ダイアログ防止)
                    self.frame.arm_panel_ctrl.arm_alignment_finger_flg_ctrl.SetValue(
                        0)

                    # カメラ元情報
                    self.frame.camera_panel_ctrl.initialize(event)
                    self.frame.camera_panel_ctrl.camera_set_dict[
                        now_motion_idx +
                        1].camera_model_file_ctrl.file_ctrl.SetPath(
                            org_camera_model_path)
                    self.frame.camera_panel_ctrl.camera_set_dict[
                        now_motion_idx +
                        1].camera_offset_y_ctrl.SetValue(camera_y_offset_datas)

                    # 出力パス変更
                    self.frame.multi_panel_ctrl.file_set_list[
                        now_motion_idx - 1].set_output_vmd_path(event)

        # 一旦リリース
        self.frame.release_tab()
        # ファイルタブに移動
        self.frame.note_ctrl.ChangeSelection(
            self.frame.file_panel_ctrl.tab_idx)
        # フォーム無効化
        self.frame.file_panel_ctrl.disable()
        # タブ固定
        self.frame.file_panel_ctrl.fix_tab()

        # ファイルタブのコンソール
        sys.stdout = self.frame.file_panel_ctrl.console_ctrl

        self.frame.elapsed_time = 0
        result = True
        result = self.frame.is_valid() and result

        if not result:
            # タブ移動可
            self.frame.release_tab()
            # フォーム有効化
            self.frame.enable()

            return result

        # 読み込み開始
        if self.frame.load_worker:
            logger.error("まだ処理が実行中です。終了してから再度実行してください。",
                         decoration=MLogger.DECORATION_BOX)
        else:
            # 停止ボタンに切り替え
            self.frame.file_panel_ctrl.check_btn_ctrl.SetLabel("読み込み処理停止")
            self.frame.file_panel_ctrl.check_btn_ctrl.Enable()

            # 別スレッドで実行(次行がない場合、-1で終了フラグ)
            self.frame.load_worker = LoadWorkerThread(
                self.frame, BulkLoadThreadEvent,
                row_no if row_no > line_idx else -1, True, False, False)
            self.frame.load_worker.start()

        return result

    # 読み込み完了処理
    def on_load_result(self, event: wx.Event):
        self.frame.elapsed_time = event.elapsed_time

        # タブ移動可
        self.frame.release_tab()
        # フォーム有効化
        self.frame.enable()
        # ワーカー終了
        self.frame.load_worker = None
        # プログレス非表示
        self.frame.file_panel_ctrl.gauge_ctrl.SetValue(0)

        if not event.result:
            # 終了音を鳴らす
            self.frame.sound_finish()

            event.Skip()
            return False

        result = self.frame.is_loaded_valid()

        if not result:
            # タブ移動可
            self.frame.release_tab()
            # フォーム有効化
            self.frame.enable()

            event.Skip()
            return False

        logger.info("ファイルデータ読み込みが完了しました",
                    decoration=MLogger.DECORATION_BOX,
                    title="OK")

        # フォーム無効化
        self.frame.file_panel_ctrl.disable()
        # タブ固定
        self.frame.file_panel_ctrl.fix_tab()

        if self.frame.worker:
            logger.error("まだ処理が実行中です。終了してから再度実行してください。",
                         decoration=MLogger.DECORATION_BOX)
        else:
            # 停止ボタンに切り替え
            self.frame.file_panel_ctrl.exec_btn_ctrl.SetLabel("VMDサイジング停止")
            self.frame.file_panel_ctrl.exec_btn_ctrl.Enable()

            # 別スレッドで実行
            self.frame.worker = SizingWorkerThread(self.frame,
                                                   BulkSizingThreadEvent,
                                                   event.target_idx,
                                                   self.frame.is_saving,
                                                   self.frame.is_out_log)
            self.frame.worker.start()

    # スレッド実行結果
    def on_exec_result(self, event: wx.Event):
        # 実行ボタンに切り替え
        self.frame.file_panel_ctrl.exec_btn_ctrl.SetLabel("VMDサイジング実行")
        self.frame.file_panel_ctrl.exec_btn_ctrl.Enable()

        if not event.result:
            # 終了音を鳴らす
            self.frame.sound_finish()

            event.Skip()
            return False

        self.frame.elapsed_time += event.elapsed_time
        worked_time = "\n処理時間: {0}".format(self.frame.show_worked_time())
        logger.info(worked_time)

        if self.frame.is_out_log and event.output_log_path and os.path.exists(
                event.output_log_path):
            # ログ出力対象である場合、追記
            with open(event.output_log_path, mode='a', encoding='utf-8') as f:
                f.write(worked_time)

        # ワーカー終了
        self.frame.worker = None

        if event.target_idx >= 0:
            # 次のターゲットがある場合、次を処理
            logger.info("\n----------------------------------")

            return self.load(event, event.target_idx)

        # ファイルタブのコンソール
        sys.stdout = self.frame.file_panel_ctrl.console_ctrl

        # 終了音を鳴らす
        self.frame.sound_finish()

        # ファイルタブのコンソール
        if sys.stdout != self.frame.file_panel_ctrl.console_ctrl:
            sys.stdout = self.frame.file_panel_ctrl.console_ctrl

        # Bulk用データ消去
        self.frame.morph_panel_ctrl.bulk_morph_set_dict = {}
        self.frame.arm_panel_ctrl.bulk_avoidance_set_dict = {}
        self.frame.camera_panel_ctrl.bulk_camera_set_dict = {}

        # タブ移動可
        self.frame.release_tab()
        # フォーム有効化
        self.frame.enable()
        # プログレス非表示
        self.frame.file_panel_ctrl.gauge_ctrl.SetValue(0)

        logger.info("全てのサイジング処理が終了しました",
                    decoration=MLogger.DECORATION_BOX,
                    title="一括処理")
Ejemplo n.º 17
0
    def __init__(self, frame: wx.Frame, parent: wx.Notebook, tab_idx: int):
        super().__init__(frame, parent, tab_idx)

        self.description_txt = wx.StaticText(self, wx.ID_ANY,
                                             "設定を一括で指定して、連続して処理させる事ができます。",
                                             wx.DefaultPosition,
                                             wx.DefaultSize, 0)
        self.sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                         wx.DefaultSize, wx.LI_HORIZONTAL)
        self.sizer.Add(self.static_line, 0, wx.EXPAND | wx.ALL, 5)

        # バルクBULKファイルコントロール
        self.bulk_csv_file_ctrl = HistoryFilePickerCtrl(frame, self, u"一括処理用CSV", u"一括処理用CSVファイルを開く", ("csv"), wx.FLP_DEFAULT_STYLE, \
                                                        u"一括処理用のCSVを指定してください。\nフォーマットは、DLボタンから取得できます。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                        file_model_spacer=0, title_parts_ctrl=None, title_parts2_ctrl=None, \
                                                        file_histories_key="bulk_csv", is_change_output=False, is_aster=False, is_save=False, set_no=0)
        self.sizer.Add(self.bulk_csv_file_ctrl.sizer, 0, wx.EXPAND | wx.ALL, 0)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 一括サイジング確認ボタン
        self.check_btn_ctrl = wx.Button(self, wx.ID_ANY,
                                        u"一括サイジング確認", wx.DefaultPosition,
                                        wx.Size(200, 50), 0)
        self.check_btn_ctrl.SetToolTip(u"指定されたCSVデータの設定を確認します。")
        self.check_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        self.check_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_check_click)
        btn_sizer.Add(self.check_btn_ctrl, 0, wx.ALL, 5)

        # 一括サイジング実行ボタン
        self.bulk_btn_ctrl = wx.Button(self, wx.ID_ANY, u"一括サイジング実行",
                                       wx.DefaultPosition, wx.Size(200, 50), 0)
        self.bulk_btn_ctrl.SetToolTip(u"一括でサイジングを実行します")
        self.bulk_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        self.bulk_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_bulk_click)
        btn_sizer.Add(self.bulk_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.fit()

        # 変換完了処理バインド
        self.frame.Bind(EVT_BULK_LOAD_THREAD, self.on_load_result)
        self.frame.Bind(EVT_BULK_SIZING_THREAD, self.on_exec_result)
Ejemplo n.º 18
0
    def __init__(self, frame: wx.Frame, multi_join: wx.Notebook, tab_idx: int):
        super().__init__(frame, multi_join, tab_idx)
        self.timer = wx.Timer(self, TIMER_ID)
        self.convert_multi_join_worker = None

        self.header_sizer = wx.BoxSizer(wx.VERTICAL)

        self.description_txt = wx.StaticText(self, wx.ID_ANY, u"モーションの指定ボーンの移動量XYZと回転量XYZを統合します。\n" \
                                             + "統合するボーンは「ボーン指定」ボタンから定義できます。多段分割の設定エクスポートファイル流用可能です。" \
                                             + "\n不要キー削除を行うと、キーが間引きされます。キー間がオリジナルから多少ずれ、やや時間がかかります。", wx.DefaultPosition, wx.DefaultSize, 0)
        self.header_sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                           wx.DefaultSize, wx.LI_HORIZONTAL)
        self.header_sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5)

        # 対象VMDファイルコントロール
        self.vmd_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"対象モーションVMD/VPD", u"対象モーションVMDファイルを開く", ("vmd", "vpd"), wx.FLP_DEFAULT_STYLE, \
                                                   u"調整したい対象モーションのVMDパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                   file_model_spacer=46, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="multi_join_vmd", is_change_output=True, \
                                                   is_aster=False, is_save=False, set_no=1)
        self.vmd_file_ctrl.file_ctrl.Bind(wx.EVT_FILEPICKER_CHANGED,
                                          self.on_change_file)
        self.header_sizer.Add(self.vmd_file_ctrl.sizer, 1, wx.EXPAND, 0)

        # 対象PMXファイルコントロール
        self.model_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"多段化済みモデルPMX", u"多段化済みモデルPMXファイルを開く", ("pmx"), wx.FLP_DEFAULT_STYLE, \
                                                     u"モーションを適用したい多段化済みモデルのPMXパスを指定してください。\n人体モデル以外にも適用可能です。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                     file_model_spacer=49, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="multi_join_pmx", \
                                                     is_change_output=True, is_aster=False, is_save=False, set_no=1)
        self.model_file_ctrl.file_ctrl.Bind(wx.EVT_FILEPICKER_CHANGED,
                                            self.on_change_file)
        self.header_sizer.Add(self.model_file_ctrl.sizer, 1, wx.EXPAND, 0)

        # 出力先VMDファイルコントロール
        self.output_vmd_file_ctrl = BaseFilePickerCtrl(frame, self, u"出力対象VMD", u"出力対象VMDファイルを開く", ("vmd"), wx.FLP_OVERWRITE_PROMPT | wx.FLP_SAVE | wx.FLP_USE_TEXTCTRL, \
                                                       u"調整結果の対象VMD出力パスを指定してください。\n対象VMDファイル名に基づいて自動生成されますが、任意のパスに変更することも可能です。", \
                                                       is_aster=False, is_save=True, set_no=1)
        self.header_sizer.Add(self.output_vmd_file_ctrl.sizer, 1, wx.EXPAND, 0)

        self.sizer.Add(self.header_sizer, 0, wx.EXPAND | wx.ALL, 5)

        self.setting_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # ボーン名指定
        self.bone_target_txt_ctrl = wx.TextCtrl(
            self, wx.ID_ANY, "", wx.DefaultPosition, (450, 50),
            wx.HSCROLL | wx.VSCROLL | wx.TE_MULTILINE | wx.TE_READONLY)
        self.bone_target_txt_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.setting_sizer.Add(self.bone_target_txt_ctrl, 1,
                               wx.EXPAND | wx.ALL, 5)

        self.bone_target_btn_ctrl = wx.Button(self, wx.ID_ANY, u"ボーン指定",
                                              wx.DefaultPosition,
                                              wx.DefaultSize, 0)
        self.bone_target_btn_ctrl.SetToolTip(
            u"モーションに登録されているボーンから、統合したいボーンを指定できます")
        self.bone_target_btn_ctrl.Bind(wx.EVT_BUTTON,
                                       self.on_click_bone_target)
        self.setting_sizer.Add(self.bone_target_btn_ctrl, 0,
                               wx.ALIGN_BOTTOM | wx.ALL, 5)

        self.sizer.Add(self.setting_sizer, 0, wx.EXPAND | wx.ALL, 5)

        # 不要キー削除処理
        self.flg_sizer = wx.BoxSizer(wx.VERTICAL)
        self.remove_unnecessary_flg_ctrl = wx.CheckBox(self, wx.ID_ANY,
                                                       u"不要キー削除処理を追加実行する",
                                                       wx.DefaultPosition,
                                                       wx.DefaultSize, 0)
        self.remove_unnecessary_flg_ctrl.SetToolTip(
            u"チェックを入れると、不要キー削除処理を追加で実行します。キーが減る分、キー間が少しズレる事があります。")
        self.remove_unnecessary_flg_ctrl.Bind(wx.EVT_CHECKBOX,
                                              self.on_change_file)
        self.flg_sizer.Add(self.remove_unnecessary_flg_ctrl, 0, wx.ALL, 5)
        self.sizer.Add(self.flg_sizer, 0, wx.EXPAND | wx.ALL, 5)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 実行ボタン
        self.multi_join_btn_ctrl = wx.Button(self, wx.ID_ANY, u"多段統合",
                                             wx.DefaultPosition,
                                             wx.Size(200, 50), 0)
        self.multi_join_btn_ctrl.SetToolTip(u"キーフレを多段用に統合したモーションを生成します")
        self.multi_join_btn_ctrl.Bind(wx.EVT_LEFT_DOWN,
                                      self.on_convert_multi_join)
        self.multi_join_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        btn_sizer.Add(self.multi_join_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.Layout()
        self.fit()

        # ボーン選択用ダイアログ
        self.bone_dialog = TargetBoneDialog(self.frame, self, "←", "分割")

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_SMOOTH_THREAD, self.on_convert_multi_join_result)
Ejemplo n.º 19
0
class NoisePanel(BasePanel):
    def __init__(self, frame: wx.Frame, noise: wx.Notebook, tab_idx: int):
        super().__init__(frame, noise, tab_idx)
        self.timer = wx.Timer(self, TIMER_ID)
        self.convert_noise_worker = None

        self.header_sizer = wx.BoxSizer(wx.VERTICAL)

        self.description_txt = wx.StaticText(self, wx.ID_ANY, u"モーションをゆらぎ(ノイズ)を付与して複製します。\n" \
                                             + "出力ファイル名のNxxは指定ゆらぎの大きさ、nxxxは複製連番、axxはやる気係数(身体の振りの大きさ)です。", wx.DefaultPosition, wx.DefaultSize, 0)
        self.header_sizer.Add(self.description_txt, 0, wx.ALL, 5)

        self.static_line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition,
                                           wx.DefaultSize, wx.LI_HORIZONTAL)
        self.header_sizer.Add(self.static_line01, 0, wx.EXPAND | wx.ALL, 5)

        # 対象VMDファイルコントロール
        self.noise_vmd_file_ctrl = HistoryFilePickerCtrl(self.frame, self, u"対象モーションVMD/VPD", u"対象モーションVMD/VPDファイルを開く", ("vmd", "vpd"), wx.FLP_DEFAULT_STYLE, \
                                                         u"調整したい対象モーションのVMDパスを指定してください。\nD&Dでの指定、開くボタンからの指定、履歴からの選択ができます。", \
                                                         file_model_spacer=46, title_parts_ctrl=None, title_parts2_ctrl=None, file_histories_key="noise_vmd", is_change_output=True, \
                                                         is_aster=False, is_save=False, set_no=1)
        self.header_sizer.Add(self.noise_vmd_file_ctrl.sizer, 1, wx.EXPAND, 0)

        # 出力先VMDファイルコントロール
        self.output_noise_vmd_file_ctrl = BaseFilePickerCtrl(frame, self, u"出力対象VMD", u"出力対象VMDファイルを開く", ("vmd"), wx.FLP_OVERWRITE_PROMPT | wx.FLP_SAVE | wx.FLP_USE_TEXTCTRL, \
                                                             u"調整結果の対象VMD出力パスを指定してください。\n対象VMDファイル名に基づいて自動生成されますが、任意のパスに変更することも可能です。", \
                                                             is_aster=False, is_save=True, set_no=1)
        self.header_sizer.Add(self.output_noise_vmd_file_ctrl.sizer, 1,
                              wx.EXPAND, 0)

        self.sizer.Add(self.header_sizer, 0, wx.EXPAND | wx.ALL, 5)

        self.setting_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # ゆらぎの大きさ
        self.noise_size_txt = wx.StaticText(self, wx.ID_ANY, u"ゆらぎの大きさ",
                                            wx.DefaultPosition, wx.DefaultSize,
                                            0)
        self.setting_sizer.Add(self.noise_size_txt, 0, wx.ALL, 5)

        self.noise_size_ctrl = wx.SpinCtrl(self,
                                           id=wx.ID_ANY,
                                           size=wx.Size(60, -1),
                                           value="8",
                                           min=0,
                                           max=99999999,
                                           initial=8)
        self.noise_size_ctrl.SetToolTip(
            u"ゆらぎの大きさを指定して下さい。値が大きいほど、ゆらぎが大きくなります。")
        self.noise_size_ctrl.Bind(wx.EVT_SPINCTRL, self.on_change_file)
        self.setting_sizer.Add(self.noise_size_ctrl, 0, wx.ALL, 5)

        # 複製数
        self.copy_cnt_txt = wx.StaticText(self, wx.ID_ANY, u"複製数",
                                          wx.DefaultPosition, wx.DefaultSize,
                                          0)
        self.setting_sizer.Add(self.copy_cnt_txt, 0, wx.ALL, 5)

        self.copy_cnt_ctrl = wx.SpinCtrl(self,
                                         id=wx.ID_ANY,
                                         size=wx.Size(60, -1),
                                         value="2",
                                         min=1,
                                         max=99999999,
                                         initial=2)
        self.copy_cnt_ctrl.SetToolTip(u"複製する数を指定して下さい。")
        self.copy_cnt_ctrl.Bind(wx.EVT_SPINCTRL, self.on_change_file)
        self.setting_sizer.Add(self.copy_cnt_ctrl, 0, wx.ALL, 5)

        # やる気係数
        self.motivation_flg_ctrl = wx.CheckBox(self, wx.ID_ANY, u"やる気係数を適用する",
                                               wx.DefaultPosition,
                                               wx.DefaultSize, 0)
        self.motivation_flg_ctrl.SetToolTip(
            u"チェックを入れると、やる気係数もランダムに発生します。\nやる気係数は値が大きいほどモーションの振り幅が大きくなります。\n値が小さいほどモーションの振り幅が小さくなります。"
        )
        self.setting_sizer.Add(self.motivation_flg_ctrl, 0, wx.ALL, 5)

        # 指ゆらぎ
        self.finger_noise_flg_ctrl = wx.CheckBox(self, wx.ID_ANY,
                                                 u"指にもゆらぎを適用する",
                                                 wx.DefaultPosition,
                                                 wx.DefaultSize, 0)
        self.finger_noise_flg_ctrl.SetToolTip(
            u"チェックを入れると、「指」をボーン名に含むボーンも揺らがせます。")
        self.setting_sizer.Add(self.finger_noise_flg_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(self.setting_sizer, 0, wx.EXPAND | wx.ALL, 5)

        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 実行ボタン
        self.noise_btn_ctrl = wx.Button(self, wx.ID_ANY,
                                        u"ゆらぎ複製", wx.DefaultPosition,
                                        wx.Size(200, 50), 0)
        self.noise_btn_ctrl.SetToolTip(u"ゆらぎを付与したモーションを複製します")
        self.noise_btn_ctrl.Bind(wx.EVT_LEFT_DOWN, self.on_convert_noise)
        self.noise_btn_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.on_doubleclick)
        btn_sizer.Add(self.noise_btn_ctrl, 0, wx.ALL, 5)

        self.sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.SHAPED, 5)

        # コンソール
        self.console_ctrl = ConsoleCtrl(self, self.frame.logging_level, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(-1, 420), \
                                        wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.HSCROLL | wx.VSCROLL | wx.WANTS_CHARS)
        self.console_ctrl.SetBackgroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.console_ctrl.Bind(
            wx.EVT_CHAR,
            lambda event: MFormUtils.on_select_all(event, self.console_ctrl))
        self.sizer.Add(self.console_ctrl, 1, wx.ALL | wx.EXPAND, 5)

        # ゲージ
        self.gauge_ctrl = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition,
                                   wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge_ctrl.SetValue(0)
        self.sizer.Add(self.gauge_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        self.Layout()
        self.fit()

        # フレームに変換完了処理バインド
        self.frame.Bind(EVT_SMOOTH_THREAD, self.on_convert_noise_result)

    def on_wheel_spin_ctrl(self, event: wx.Event, inc=1):
        # self.frame.on_wheel_spin_ctrl(event, inc)
        self.set_output_vmd_path(event)

    # ファイル変更時の処理
    def on_change_file(self, event: wx.Event):
        self.set_output_vmd_path(event, is_force=True)

    def set_output_vmd_path(self, event, is_force=False):
        output_noise_vmd_path = MFileUtils.get_output_noise_vmd_path(
            self.noise_vmd_file_ctrl.file_ctrl.GetPath(),
            self.output_noise_vmd_file_ctrl.file_ctrl.GetPath(),
            self.noise_size_ctrl.GetValue(), is_force)

        self.output_noise_vmd_file_ctrl.file_ctrl.SetPath(
            output_noise_vmd_path)

        if len(output_noise_vmd_path) >= 255 and os.name == "nt":
            logger.error("生成予定のファイルパスがWindowsの制限を超えています。\n生成予定パス: {0}".format(
                output_noise_vmd_path),
                         decoration=MLogger.DECORATION_BOX)

    # フォーム無効化
    def disable(self):
        self.noise_vmd_file_ctrl.disable()
        self.output_noise_vmd_file_ctrl.disable()
        self.noise_btn_ctrl.Disable()

    # フォーム無効化
    def enable(self):
        self.noise_vmd_file_ctrl.enable()
        self.output_noise_vmd_file_ctrl.enable()
        self.noise_btn_ctrl.Enable()

    def on_doubleclick(self, event: wx.Event):
        self.timer.Stop()
        logger.warning("ダブルクリックされました。", decoration=MLogger.DECORATION_BOX)
        event.Skip(False)
        return False

    # 多段分割変換
    def on_convert_noise(self, event: wx.Event):
        self.timer.Start(200)
        self.Bind(wx.EVT_TIMER, self.on_convert, id=TIMER_ID)

    # 多段分割変換
    def on_convert(self, event: wx.Event):
        self.timer.Stop()
        self.Unbind(wx.EVT_TIMER, id=TIMER_ID)
        # フォーム無効化
        self.disable()
        # タブ固定
        self.fix_tab()
        # コンソールクリア
        self.console_ctrl.Clear()
        # 出力先を多段分割パネルのコンソールに変更
        sys.stdout = self.console_ctrl

        self.noise_vmd_file_ctrl.save()

        # JSON出力
        MFileUtils.save_history(self.frame.mydir_path,
                                self.frame.file_hitories)

        self.elapsed_time = 0
        result = True
        result = self.noise_vmd_file_ctrl.is_valid() and result

        if not result:
            # 終了音
            self.frame.sound_finish()
            # タブ移動可
            self.release_tab()
            # フォーム有効化
            self.enable()

            return result

        # ゆらぎ複製変換開始
        if self.noise_btn_ctrl.GetLabel(
        ) == "ゆらぎ複製停止" and self.convert_noise_worker:
            # フォーム無効化
            self.disable()
            # 停止状態でボタン押下時、停止
            self.convert_noise_worker.stop()

            # タブ移動可
            self.frame.release_tab()
            # フォーム有効化
            self.frame.enable()
            # ワーカー終了
            self.convert_noise_worker = None
            # プログレス非表示
            self.gauge_ctrl.SetValue(0)

            logger.warning("ゆらぎ複製を中断します。", decoration=MLogger.DECORATION_BOX)
            self.noise_btn_ctrl.SetLabel("ゆらぎ複製")

            event.Skip(False)
        elif not self.convert_noise_worker:
            # フォーム無効化
            self.disable()
            # タブ固定
            self.fix_tab()
            # コンソールクリア
            self.console_ctrl.Clear()
            # ラベル変更
            self.noise_btn_ctrl.SetLabel("ゆらぎ複製停止")
            self.noise_btn_ctrl.Enable()

            self.convert_noise_worker = NoiseWorkerThread(
                self.frame, NoiseThreadEvent, self.frame.is_saving,
                self.frame.is_out_log)
            self.convert_noise_worker.start()

            event.Skip()
        else:
            logger.error("まだ処理が実行中です。終了してから再度実行してください。",
                         decoration=MLogger.DECORATION_BOX)
            event.Skip(False)

        return result

    # 多段分割変換完了処理
    def on_convert_noise_result(self, event: wx.Event):
        self.elapsed_time = event.elapsed_time
        logger.info("\n処理時間: %s", self.show_worked_time())
        self.noise_btn_ctrl.SetLabel("ゆらぎ複製")

        # 終了音
        self.frame.sound_finish()

        # タブ移動可
        self.release_tab()
        # フォーム有効化
        self.enable()
        # ワーカー終了
        self.convert_noise_worker = None
        # プログレス非表示
        self.gauge_ctrl.SetValue(0)

    def show_worked_time(self):
        # 経過秒数を時分秒に変換
        td_m, td_s = divmod(self.elapsed_time, 60)

        if td_m == 0:
            worked_time = "{0:02d}秒".format(int(td_s))
        else:
            worked_time = "{0:02d}分{1:02d}秒".format(int(td_m), int(td_s))

        return worked_time