#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
測定及び解析シーケンスの作成・編集と実行
"""
from __future__ import print_function
import datetime
import Manyo
import Manyo.Utsusemi as mu
import utsusemi.vis.UtilQt as UtilQt
import codecs
import threading
import os
import sys
import time
import platform

if platform.python_version_tuple()[0] == '2':
    import thread as _thread
else:
    import _thread
# from types import *
# import utsusemi.vis.UtilPlot as UtilPlot
PYSIDEVER = 1
try:
    from PySide6 import QtCore, QtGui, QtWidgets
    PYSIDEVER = 6
except:
    try:
        from PySide2 import QtCore, QtGui, QtWidgets
        PYSIDEVER = 2
    except:
        from PySide import QtCore, QtGui
        import PySide.QtGui as QtWidgets

if QtCore.__version__ < '5.0.0':
    from utsusemi.ana.ui_SequencerQ import Ui_MainWindow, Ui_Dialog, Ui_visframe
else:
    if PYSIDEVER == 2:
        from utsusemi.ana.ui2_SequencerQ import Ui_MainWindow, Ui_Dialog, Ui_visframe
    elif PYSIDEVER == 6:
        from utsusemi.ana.ui6_SequencerQ import Ui_MainWindow, Ui_Dialog, Ui_visframe
if mu.UtsusemiEnvGetDebugMode():
    mu.UtsusemiMessage("SequenceEditor PySide Ver={}".format(PYSIDEVER))

#######################################
#    Definitions
#######################################

VISMODULE = 'VisualModule'
INITSEQFILE = "InitialSequence.pmt"
BASEFACADEFILE = "Sequencer_fa*.py"
BASELOGFILE = "log.Sequencer2_%s_%05d"
EMPTYDAT = "EMPTYDAT"
#######################################
#  Argument
#######################################


class Argument(object):
    """
     引数データクラス
    """
    #########################################

    def __init__(self):
        """
        コンストラクタ
        @param  無し
        @retval 無し

        """
        # 引数のラベル
        self.label = ""
        # 引数の型、"string", "float", "integer", "boolean", "retval"
        self.type = ""
        # 引数の値
        self.value = ""
        # 引数の説明、ツールチップに表示
        self.tip = ""

    #########################################
    def Copy(self):
        """
        新しい引数インスタンスを作り内容をコピー
        @param  無し
        @retval コピー後の引数インスタンス
        """
        # 新しい引数インスタンスの作成
        narg = Argument()
        # 新しいインスタンスへデータをコピー
        narg.label = self.label
        narg.type = self.type
        narg.value = self.value
        narg.tip = self.tip

        return narg

#######################################
#  ReturnValue
#######################################


class ReturnValue(object):
    """
     戻り値データクラス
    """
    #########################################

    def __init__(self):
        """
        コンストラクタ
        @param  無し
        @retval 無し
        """
        # 戻り値のラベル
        self.label = None
        # 戻り値の説明、ツールチップに表示
        self.tip = ""
        # 戻り値(関数実行後に、値が入る)
        self.retResult = None

    #########################################
    def Copy(self):
        """
        新しい戻り値インスタンスを作り内容をコピー
        @param  無し
        @retval コピー後の戻り値インスタンス
        """
        # 新しい戻り値インスタンスの作成
        nret = ReturnValue()
        # 新しいインスタンスへデータをコピー
        nret.label = self.label
        nret.tip = self.tip
        nret.retResult = self.retResult
        return nret

#######################################
#  Function
#######################################


class Function(object):
    """
     関数データクラス
    """
    #########################################

    def __init__(self):
        """
        コンストラクタ
        @param  無し
        @retval 無し
        """
        # 関数名称(モジュール名付き)
        self.mfname = ""
        # 関数の実行アドレス
        self.fobj = None
        # 関数ドキュメント
        self.fdoc = ""
        # 引数データインスタンスのリスト
        self.args = []
        # 戻り値データのインスタンス
        self.retn = None
        # ステップ状態  -1:未実行(実行不可) 0: 未実行(実行可)、1: 実行中、2:正常終了、3: 異常終了
        self.stat = 0
        # 開始時間(文字列)
        self.beginTime = ""
        # 終了時間(文字列)
        self.endTime = ""

    #########################################
    def Copy(self):
        """
        新しい関数インスタンスを作り内容をコピー
        @param  無し
        @retval コピー後の関数インスタンス
        """
        # 新しい関数インスタンスの作成
        nfunc = Function()
        nfunc.mfname = self.mfname
        nfunc.fobj = self.fobj
        # 引数をコピー
        for arg in self.args:
            narg = arg.Copy()
            nfunc.args.append(narg)
        # 戻り値をコピー
        if self.retn is not None:
            nfunc.retn = self.retn.Copy()

        return nfunc

    #########################################
    def ClearResult(self):
        """
        関数の実行結果をクリア
        @param  無し
        @retval 無し
        """
        self.stat = 0
        if self.retn is not None:
            del self.retn.retResult
            self.retn.retResult = None
        self.beginTime = ""
        self.endTime = ""

#######################################
#  ExecSequence
#######################################


class ExecSequence(object):
    """
     サブスレッド上で、シーケンスを実行する
    """

    #########################################
    def __init__(self, lock):
        """
        コンストラクタ
        @param  lock  シーケンス排他制御用のロック
        @retval 無し
        """
        self.lock = lock
        # 初期化
        self.rd = Rendering(None)
        self.stopflag = False
        # 実行状態　-1:停止状態  0:実行開始開始前  1:実行中
        self.status = -1
        # 実行中ステップ番号
        self.execNo = 0
        # エラー状態、0:エラー無し  1:実行前エラー  2: 実行中エラー
        self.erstatus = 0
        # エラーメッセージ
        self.errmsg = ""
        self.LogFile = None
    #############################################

    def GetTimeNow(self):
        """
        現在の時刻を、文字列形式で返す
        @param  無し
        @retval 時刻文字列　
        """
        # 時間を取得
        tplTime = time.localtime()
        # 時:分:秒　の文字列を作成
        strTime = "%2d:%02d:%02d" % (tplTime[3], tplTime[4], tplTime[5])

        return strTime

    #############################################
    def MakeHistory(self):
        """
        現在の行以前のスクリプトを返す
        @param  無し
        @retval 実行履歴(マルチ文字列)　
        """
        history = ""
        # 最初の行か
        if self.execNum > 0:
            # 現在の行の前まで
            for i in range(self.execNum):
                func = self.funcs[i]
                line = self.rd.MakeLine(func)
                history = history + line + "\n"

        return history

    #############################################

    def OnStart(self, index, funcs):
        """
        一連の関数の実行開始
        @param  ndex  開始インデックス
        @param  funcs  関数リスト
        @retval 無し
        """
        self.status = 0
        self.erstatus = 0
        self.funcs = funcs
        # 指定行からスタート
        self.execNum = index
        self.stopflag = False

        # サブスレッドスタート
        th = threading.Thread(target=self.__ExecFunc, args=(self.funcs,))
        #th.setDaemon(True)
        th.daemon = True
        th.start()

    #############################################
    def OnStop(self):
        """
        一連の関数の実行停止
        @param  無し
        @retval 無し
        """
        self.stopflag = True  # ユーザ停止

    #############################################
    def __ExecFunc(self, funcs):
        """
        サブスレッドにて、要求された関数群を実行する
        @param  funcs 関数リスト
        @retval 無し
        """
        self.status = 1
        numFuncs = len(funcs)
        # 関数リスト中の関数を1個づつ実行

        while self.execNum < numFuncs:
            # シーケンスリストから関数を取得
            self.lock.acquire()
            func = funcs[self.execNum]

            if self.LogFile is None or mu.UtsusemiEnvGetDebugMode():
                pass
            else:
                try:
                    ff = open(self.LogFile, "a")
                    dt = datetime.datetime.now()
                    msg = "[%s] %s\n" % (dt.strftime(
                        "%Y%m%d%H%M%S"), func.mfname)
                    msg += "      Args: "
                    for arg in func.args:
                        msg += "%s=%s " % (arg.label, str(arg.value))
                    msg += "\n"
                    ff.write(msg)
                    ff.close()
                except:
                    self.LogFile = None

            # 引数リストを準備
            execargs = []
            # 引数リストを解析
            for arg in func.args:
                # 引数が特別予約語: 履歴要求か
                if arg.label == "HISTORY":
                    execarg = self.MakeHistory()
                else:
                    # 引数を解釈して実行形式に変換
                    execarg = self.rd.ChangeArg(arg, self.funcs)
                # 正しく変換されていなければ
                if execarg is None and (arg.value != EMPTYDAT):
                    # エラー中止
                    self.lock.release()
                    self.erstatus = 1
                    self.ermessage = "\nArgument %s of %s error!!" % (
                        arg.label, func.mfname)
                    break
                execargs.append(execarg)

            # 中断または中止要求があれば、
            if self.erstatus > 0 or self.stopflag:
                break
            # 開始時間を設定
            func.beginTime = self.GetTimeNow()
            # 関数を実行
            func.stat = 1    # 実行中
            fobj = func.fobj
            self.lock.release()
            try:
                # 関数の実行
                time.sleep(0.6)
                ret = fobj(*execargs)
            # ユーザワーニングか
            except UserWarning as ex:
                # エラー中止
                self.ermessage = "%s: \n%s" % (func.mfname, ex)
                self.erstatus = 2  # 実行中エラー
                break
            # ユーザワーニング以外の例外発生
            except:
                # エラー中止
                self.ermessage = func.mfname
                self.erstatus = 2  # 実行中エラー
                func.stat = 3
                # 端末に標準エラーを表示するために、例外を再発生
                raise
                break
            else:
                self.lock.acquire()

                # 実行中にステップ状態が変更されてないかったなら
                if func.stat == 1:
                    # 関数の戻り値が指定されているなら
                    if func.retn is not None:
                        if ret is not None:
                            # 実行結果を関数リストに入れる
                            func.retn.retResult = ret
                    # 終了時間を設定
                    func.endTime = self.GetTimeNow()
                    func.stat = 2    # 正常終了

                self.lock.release()
                # 関数実行中に中止要求があったか
                if self.stopflag:
                    break
                # 実行中ステップのインデックスをインクリメント
                self.execNum += 1
        # 異常終了か
        if self.erstatus:
            self.lock.acquire()
            func.stat = 3    # 異常終了

            self.lock.release()
        # シーケンスの終了

        self.status = -1

    #########################################

    def SetLogFileName(self, fname):
        """
        """
        self.LogFile = fname


#######################################
# FunctionTree
#######################################


class FunctionTree(object):
    """
     関数ツリークラス

    """
    #########################################

    def __init__(self, frame):
        """
        コンストラクタ
        @param  frame   親ウィンドウ(mainframe)のID
        @retval 無し
        """
        self.frame = frame

    #########################################
    def MakeNode(self, tree, rootName, modules):
        """
        関数ツリーのノード作成
        @param  tree      ツリーのIDインスタンス
        @param  rootName  ツリーの root 名称文字列
        @param  modules   ファサードモジュールリスト
        @retval 関数登録リスト
        """
        self.tree = tree
        # ファサードモジュールクラスのインスタンスを取得
        self.fm = FacadeModule()
        # 関数登録リストの準備
        self.rgfuncs = []
        # ルートノードを作成
        root = QtWidgets.QTreeWidgetItem(self.tree)
        root.setText(0, rootName)
        # ルートの直下のノードを作成
        for module in modules:

            # 文字列 = モジュール名か
            if isinstance(module, str):
                # サブノードを作成
                self.MakeSubNode(root, module)
            # リストまたはタプルであればモジュール群
            elif isinstance(module, tuple) or isinstance(module, list):

                # モジュール名に文字列を入力し空白のみでなければ
                if isinstance(module[0], str) and len(module[0].strip()) > 0:
                    # モジュール群のカテゴリー名をサブノードに作る
                    subID = QtWidgets.QTreeWidgetItem(root)
                    subID.setText(0, module[0])
                    # サブノードの下に各モジュールのノードを作成
                    for modl in module[1]:
                        # サブノードを作成
                        self.MakeSubNode(subID, modl)
                    self.tree.expandItem(subID)

        # Functions のサブノードを展開
        self.tree.expandItem(root)

        return self.rgfuncs

    #########################################
    def MakeSubNode(self, parentNode, module):
        """
        関数一覧ツリーのサブノード作成
        @param  parentNode  親ノードのID
        @param  module      インポート対象のモジュール
        @retval 無し
        """
        # 文字列でなければ
        if not isinstance(module, str):
            return

        # 関数登録モジュールのインポートとスクリプトファイルの読み込み
        try:
            funcs = self.fm.GetFuncs(module)
        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self.frame, ex)
            return

        # モジュール名があれば
        if funcs is not None:
            # サブノード作成
            subID = QtWidgets.QTreeWidgetItem(parentNode)
            subID.setText(0, module)

            # サブノードの下へ関数名を登録
            for func in funcs:
                tmp = QtWidgets.QTreeWidgetItem(subID)
                tmp.setText(0, func.mfname)
            # リストに追加
            self.rgfuncs.extend(funcs)

    #########################################
    def MakeStructuredNode(self, tree, rootName, modules):
        """
        関数ツリーのノード作成
        @param  tree      ツリーのIDインスタンス
        @param  rootName  ツリーの root 名称文字列
        @param  modules   ファサードモジュールリスト
        @retval 関数登録リスト
        """
        self.tree = tree
        # ファサードモジュールクラスのインスタンスを取得
        self.fm = FacadeModule()
        # 関数登録リストの準備
        self.rgfuncs = []
        # ルートノードを作成
        root = QtWidgets.QTreeWidgetItem(self.tree)
        root.setText(0, rootName)

        isNotOriginalSub = True
        for mod_group, a_module in modules:
            # ルートの直下のノードを作成
            for module in a_module:
                # print("#[inamura 220329] module = ",module)
                # 文字列 = モジュール名か
                if isinstance(module, str):
                    # サブノードを作成
                    if mod_group == "STRUCTURED":
                        self.MakeStructuredSubNode(root, module)
                    elif mod_group == "Visualizer":
                        visID = QtWidgets.QTreeWidgetItem(root)
                        visID.setText(0, mod_group)
                        self.MakeSubNode(visID, module)
                    else:
                        if isNotOriginalSub:
                            originID = QtWidgets.QTreeWidgetItem(root)
                            originID.setText(0, "Original")
                            isNotOriginalSub = False
                        self.MakeSubNode(originID, module)
                # リストまたはタプルであればモジュール群
                elif isinstance(module, tuple) or isinstance(module, list):

                    # モジュール名に文字列を入力し空白のみでなければ
                    if isinstance(module[0], str) and len(module[0].strip()) > 0:
                        # モジュール群のカテゴリー名をサブノードに作る
                        subID = QtWidgets.QTreeWidgetItem(root)
                        subID.setText(0, module[0])
                        # サブノードの下に各モジュールのノードを作成
                        for modl in module[1]:
                            # サブノードを作成
                            if mod_group == "STRUCTURED":
                                self.MakeStructuredSubNode(subID, modl)
                            else:
                                self.MakeSubNode(subID, modl)

        # Functions のサブノードを展開
        self.tree.expandItem(root)

        return self.rgfuncs

    #########################################
    def MakeStructuredSubNode(self, parentNode, method):
        """
        Add method to parentNode
        @param  parentNode  ID of parentNode
        @param  method      method to be imported
        @retval 無し
        """
        # 文字列でなければ
        if not isinstance(method, str):
            return

        # 関数登録モジュールのインポートとスクリプトファイルの読み込み
        try:
            func = self.fm.GetSingleFunc(method)
        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self.frame, ex)
            return

        added_method = QtWidgets.QTreeWidgetItem(parentNode)
        added_method.setText(0, method)
        self.rgfuncs.append(func)
        return

#######################################
# Rendering
#######################################


class Rendering(object):
    """
     シーケンス解析クラス

    """
    #########################################

    def __init__(self, frame):
        """
        コンストラクタ
        @param  frame   親ウィンドウ(mainframe)のID
        @retval 無し
        """
        self.frame = frame

    ##########################################
    def RenderLine(self, rgfuncs, line):
        """
        関数行を解釈
        @param  funcs 関数ディクショナリ
        @param  line 1行の文字列
        @retval 関数データ
        """
        cnt = line.find('=')
        cnt0 = line.find('(')
        # 戻り値が指定されているか
        if cnt > 0 and cnt < cnt0:
            retStr = line[:cnt]
            func = line[(cnt + 1):]
            retStr = retStr.strip()
        else:
            retStr = None
            func = line
        fflag = True
        n = func.find('(')
        funcName = func[:n].strip()

        # 関数一覧から検索
        for func in rgfuncs:
            # 関数名称か

            if func.mfname == funcName:
                fflag = False
                break

        try:
            # 登録関数名になければ
            if fflag:
                # 不正な関数名
                raise UtilQt.PlotException('Sequencer', 'S005', (funcName,))
        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self.frame, ex)
            return None

        ofunc = func.Copy()

        # 引数を取り出す
        n0 = line.find('(')
        n1 = line.rfind(')')
        args = line[(n0 + 1):n1]

        # store string argument and replace it with keyword
        re_args = ""
        ind = 0
        st_param_ind = 0
        st_param_dic = {}
        key_arg_base = "SEQUENCERPARAM"
        while(ind < len(args)):
            a_char = args[ind]
            if (a_char == '"'):
                key_arg = "%s%02d" % (key_arg_base, st_param_ind)
                ind2 = args[(ind + 1):].find('"')
                st_param_dic[key_arg] = args[(ind + 1):(ind + 1 + ind2)]
                re_args += key_arg
                ind = (ind + 1) + (ind2 + 1)
                if ind < (len(args) - 1):
                    re_args += args[ind]
                st_param_ind += 1
            else:
                re_args += a_char
            ind += 1

        # separate each argument and check the number of arguments
        param_set_list = re_args.split(",")
        try:
            if (len(param_set_list) > len(ofunc.args)):
                raise UtilQt.PlotException('Sequencer', 'S006', (funcName,))
        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self.frame, ex)

        # analysis of each argument
        isUsedArgDict = {}
        for arg in ofunc.args:
            isUsedArgDict[arg.label] = False

        ind_in_args = 0
        for a_param_set in param_set_list:
            ind2 = a_param_set.find("=")
            if ind2 != -1:
                # an argument is <label>=<val> format
                arg_label = a_param_set[:ind2]
                arg_val = a_param_set[(ind2 + 1):]
                arg_lavel = arg_label.strip()
                arg_val = arg_val.strip()
                for arg in ofunc.args:
                    if arg.label == arg_label:
                        if (arg_val.find(key_arg_base) == 0):
                            arg_val = st_param_dic[arg_val]
                        self.CheckArgType(arg, arg_val)
                        isUsedArgDict[arg.label] = True
                        break
                    else:
                        pass
            else:
                # an argument has only <val>
                while(True):
                    # seek empty argument
                    if isUsedArgDict[ofunc.args[ind_in_args].label]:
                        ind_in_args += 1
                        if ind_in_args >= len(ofunc.args):
                            raise UtilQt.PlotException(
                                'Sequencer', 'S006', (funcName,))
                    else:
                        break
                arg_val = a_param_set.strip()
                if (arg_val.find(key_arg_base) == 0):
                    arg_val = st_param_dic[arg_val]
                self.CheckArgType(ofunc.args[ind_in_args], arg_val)
                ind_in_args += 1

        # 戻り値が指定されているなら
        if ofunc.retn is not None:
            ofunc.retn.label = retStr
        return ofunc

    #########################################

    def MakeLine(self, func, isSaveToFile=False):
        """
        リストに表示する文字列を作る
        @func    　　関数データ
        @retval      表示ストリング
        """
        # 戻り値が指定されているか
        if func.retn is None:
            strLine = ""
        else:
            strLine = func.retn.label + " = "

        strLine = strLine + func.mfname + '('
        # 引数の文字列を作る
        strArg = ""
        for arg in func.args:
            # 引数の値を取得

            item = arg.value

            # 引数の属性は文字列か
            if arg.type == "string":
                if item == "":
                    item = '""'
                elif item == '':
                    item = "''"
                else:
                    # 最初の文字が、文字列記号でなければ
                    if item[0] != '"' and item[0] != "'":
                        if item.find('"') > 0:
                            item = "'" + item + "'"
                        else:
                            item = '"' + item + '"'

            # 最初の引数か
            if len(strArg) == 0:
                if (isSaveToFile):
                    strArg = strArg + "%s=%s" % (arg.label, item)
                else:
                    strArg = strArg + item
            else:
                if (isSaveToFile):
                    strArg = strArg + ",%s=%s" % (arg.label, item)
                else:
                    strArg = strArg + ", " + item
        # 引数を加えて関数の括弧を閉じる
        strLine = strLine + strArg + ')'
        return strLine

    #########################################
    def CheckArgType(self, arg, item):
        """
        引数の型チェック
        @param  arg　　引数リスト
        @param item    引数(文字列)
        @retval True: OK, False: NG
        """
        # 入力の型チェック、ただし文字列型は型チェック無しで入れ替え
        try:
            if item == "":
                raise UtilQt.PlotException('Common', 'C013', (arg.label,))
            if arg.type == "float":
                try:
                    item = "%s" % float(item)

                except:
                    raise UtilQt.PlotException('Common', 'C027', (arg.label,))

            elif arg.type == "integer":
                try:
                    intNum = int(item)
                except:
                    raise UtilQt.PlotException('Common', 'C026', (arg.label,))
            elif arg.type == "boolean":
                if item == "True" or item == "False":
                    pass
                else:
                    raise UtilQt.PlotException('Common', 'C028', (arg.label,))
        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self.frame, ex)
            return False
        # 型チッェク OK であれば値を入れ替え
        arg.value = item
        return True

    #############################################
    def ChangeArg(self, val, funcs):
        """
        引数の解析
        @param  val  引数データ
        @param  funcs 　関数の結果リスト
        @retval 変換後の引数、None の場合はエラー
        """
        ret = None
        try:
            if val.type == "float":
                ret = float(val.value)
            elif val.type == "integer":
                ret = int(val.value)
            elif val.type == "string":
                ret = str(val.value.strip('"'))
            elif val.type == "retval":
                ret = None
                # 関数リストから戻り値を検索、同じラベルが複数ある場合、最後の結果が有効
                for func in funcs:
                    # 実行結果があれば
                    if func.retn is not None and func.retn.retResult is not None:
                        # ラベルは等しいか
                        if func.retn.label == val.value:
                            ret = func.retn.retResult
            elif val.type == "boolean":
                if val.value == "True":
                    ret = True
                elif val.value == "False":
                    ret = False
                else:
                    ret = None
        except:
            # 例外発生時、None を返す
            pass
        return ret

    #############################################
    def CheckBackwash(self, index, funcs):
        """
        変更があったステップの影響が及ぶステップの結果をクリア
        @param  index   当該ステップの戻り値を使用しているステップの結果をクリア
        @param  funcs 　関数の結果リスト
        @retval 変換後の引数、None の場合はエラー
        """

        func = funcs[index]
        # 当該ステップに戻り値がなければ
        if func.retn is None or func.retn.retResult is None:
            return
        retLabel = func.retn.label
        # 次のステップからチェック
        index += 1
        while index < len(funcs):
            for arg in funcs[index].args:
                # 引数として、変更のあったステップの結果を使用しているか
                if arg.value == retLabel:
                    # 結果をクリアするステップの影響を再帰的にチェック
                    self.CheckBackwash(index, funcs)
                    # 実行中でなければ
                    if funcs[index].stat != 1:
                        # 結果をクリア
                        funcs[index].ClearResult()
                    break
            index += 1

#######################################
# SequenceFile
#######################################


class SequenceFile(object):
    """
     シーケンスファイルクラス
    """
    #########################################

    def __init__(self, frame):
        """
        コンストラクタ
        @param  frame   親ウィンドウ(mainframe)のID
        @retval 無し
        """
        self.frame = frame
        self.fDoc = ""
        # 解析クラスのインスタンスを取得
        self.rd = Rendering(frame)

        # to keep path to be used
        self.defaultpath = os.getcwd()

    ##########################################
    def OpenSequence(self, rgFuncs, flag, isNeedDlg=True, initFile="", defPath=""):
        """
        シーケンスファイルを開き、シーケンスを読み込む
        [inamura 120321][inamura 161216]
        @param  rgFuncs  登録されている関数のディクショナリ
        @param  flag
        @param  isNeedDlg   True: Open dialog comes up
        @param  initFile    Initial file name to be used if isNeedDlg is False
        @retval (ファイル名, 関数リスト)
        """
        if defPath != "":
            self.defaultpath = defPath

        if isNeedDlg:
            # PMTファイルのパスを取得
            if self.defaultpath == "":
                pdir = UtilQt.AcqPath().GetPmtPath(flag)
            else:
                pdir = self.defaultpath

            # ファイルオープンダイアログ
            filename, filt = QtWidgets.QFileDialog().getOpenFileName(
                self.frame, u"Open Sequence File ...", pdir, filter="Sequence (*.pmt)")

            # キャンセルか
            if filename == "":
                return

            self.newfile = filename
            self.defaultpath = os.path.split(self.newfile)[0]
            fname = os.path.split(self.newfile)[1]

            # 拡張子「.pmt」がないとき「.pmt」をつける
            if fname.find('.') < 0:
                fname = fname + ".pmt"
            if self.newfile.find('.') < 0:
                self.newfile = self.newfile + ".pmt"
        else:
            if initFile == "":
                return
            self.newfile = initFile
            fname = os.path.split(initFile)
            fname = fname[1]

        # 指定されたファイルを開く
        try:
            fp = codecs.open(self.newfile, 'r', "utf-8")
        except:
            raise UtilQt.PlotException('Common', 'C006', (fname,))
            return

        funcs = []
        aFlag = True
        # 1行づつ読み込む
        while 1:
            line = fp.readline()
            # ファイルの終わりか
            if not line:
                break

            # トリプルクォーテーションか
            if '"""' in line:
                if aFlag:
                    # コメントの開始
                    aFlag = False
                else:
                    # コメントの終了
                    aFlag = True

            # 有効な行か(トリプルクォーテーションで囲まれた範囲外)
            elif aFlag:
                line = line.strip()
                # コメント行か
                if len(line) > 1 and line[0] == '#':
                    pass
                # 関数か
                elif '(' in line and ')' in line:
                    # 関数を解釈
                    func = self.rd.RenderLine(rgFuncs, line)
                    # 実行可能か
                    if func is not None:
                        # リストに1行を追加
                        funcs.append(func)

        # ファイルをクローズ
        fp.close()

        # 有効な行があるか
        if len(funcs) > 0:
            return (fname, funcs)
        else:
            # 無効なシーケンスファイル
            raise UtilQt.PlotException('Sequencer', 'S011', (fname,))
            return

    ##########################################

    def SaveSequence(self, lineFuncs, flag):
        """
        シーケンスファイルを保存
        @param  lineFuncs  関数リスト
        @retval 保存ファイル名　
        """

        # コメント入力
        self.commret = None
        cdg = CommentDialog(self.frame, self.fDoc, self.PutComment)
        # launch CommonDialog with a modal
        if PYSIDEVER == 6:
            cdg.exec()
        else:
            cdg.exec_()

        # キャンセルされたか
        if self.commret is None:
            print("-- commret = None")
            return
        # PMTファイルのパスを取得
        if self.defaultpath == "":
            pdir = UtilQt.AcqPath().GetPmtPath(flag)
        else:
            pdir = self.defaultpath

        # ファイル保存ダイアログ
        filename, filt = QtWidgets.QFileDialog().getSaveFileName(
            self.frame, u"Save Sequence File...", pdir, filter="Sequence (*.pmt)")
        if filename != "":
            self.newfile = filename
            self.defaultpath = os.path.split(self.newfile)[0]
            fname = os.path.split(self.newfile)[1]
            # 拡張子が指定されていないなら
            if fname.find('.') < 0:
                # 拡張子を付加
                fname = fname + ".pmt"
                self.newfile = self.newfile + ".pmt"

            # 誤った拡張子
            elif fname.find(".pmt") < 0:
                n0 = fname.find('.')
                n1 = self.newfile.find(fname)
                fname = fname[:n0] + ".pmt"
                self.newfile = self.newfile[:n1] + fname

            # 指定ファイルが既に存在するとき上書き確認ダイアログを表示
            """removed at 210316
            if os.path.isfile(self.newfile):
                #dialog = wx.MessageDialog(self.frame,"%s already exists. Do you want to overwrite?" %(fname),\
                #                              'File Save',wx.YES_NO|wx.ICON_QUESTION)
                msg = u"%s already exists. Do you want to overwrite?" %(fname)
                ret = QtWidgets.QMessageBox().information(None,u"Warning", msg, QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Cancel )

                # キャンセル時
                #if dialog.ShowModal() != wx.ID_YES:
                if ret == QtWidgets.QMessageBox.Cancel:
                    return
            """
            # 指定されたファイルを開く
            try:
                fp = codecs.open(self.newfile, 'w', "utf-8")
            except:
                raise UtilQt.PlotException('Common', 'C006', (fname,))

            # ヘッダー書き込み
            fp.write("#!/usr/bin/python3\n")
            fp.write("# -*- coding: utf-8 -*-\n")

            # コメント書き込み
            fp.write('"""\n')
            lines = self.commret.split("\n")
            for line in lines:
                fp.write(line + "\n")
            fp.write('"""\n')

            # シーケンスのステップ数取得
            nline = len(lineFuncs)

            # インポートモジュールのリストを作る
            modules = []
            for i in range(nline):
                # 関数を取り出す

                func = lineFuncs[i]
                # モジュール名を取り出す
                module, body = func.mfname.split('.')
                # 登録済みでなければ
                if not (module in modules):
                    # リストに登録
                    modules.append(module)
                    # インポート文を書き込み
                    fp.write("import " + module + "\n")

            fp.write("\n")
            # ダミー履歴を作る
            fp.write('HISTORY = ""\n')

            # Add EMPTYDAT
            fp.write('%s = None\n\n' % (EMPTYDAT))

            # リストの内容をファイルに書き込む
            for i in range(nline):
                line = self.rd.MakeLine(lineFuncs[i], True)
                fp.write(line + '\n')

            # ファイルをクローズ
            fp.close()
            return fname

    ##########################################
    def GetFileTime(self):
        """
        直近に開いた、または保存したファイルの
        更新日を取得する
        @param   無し
        @retval 更新日時の文字列
        """
        try:
            ftime = os.path.getmtime(self.newfile)
        except:
            return ""
        # 最終更新時間を取得

        tplTime = time.localtime(ftime)
        # 時:分:秒　の文字列を作成
        strTime = "%4d/%02d/%02d  %02d:%02d" % (
            tplTime[0], tplTime[1], tplTime[2], tplTime[3], tplTime[4])

        return strTime

    ##########################################
    def GetFileDoc(self):
        """
        直近に開いた、または保存したファイルの
        ファイルキュメントを取得する。
        ファイルの最初に出現する、トリプルクォーテーション
        で囲まれた範囲をファイルドキュメントとする。
        @param   無し
        @retval ドキュメント文字列
        """
        # 指定されたファイルを開く
        try:
            fp = codecs.open(self.newfile, 'r', "utf-8")
        except:
            raise UtilQt.PlotException('Common', 'C006', (self.newfile,))
            return

        self.fDoc = ""
        cFlag = False
        # 1行づつ読み込む
        while 1:
            line = fp.readline()
            # ファイルの終わりか
            if not line:
                break
            # トリプルクォーテーションがあるか
            if line and line.find('"""') >= 0:
                # コメントの終了

                if cFlag:
                    break
                # コメントの開始
                else:
                    cFlag = True
                    # 先頭の空白文字と行末の改行コードを削除
                    line = line.strip()
                    line = line.rstrip()
                    # トリプルクォーテーションの後に文字列があるか
                    if len(line) > 3:
                        self.fDoc = self.fDoc + line[3:] + "\n"

            # コメント内か
            elif cFlag:
                self.fDoc = self.fDoc + "   " + line
        # ファイルをクローズ
        fp.close()
        return self.fDoc

    ##########################################
    def PutComment(self, comment):
        """
        コメントダイアログOK 時のコールバック関数
        @param   comment コメント
        @retval 無し
        """
        self.commret = comment

#######################################
#  ExecVisualize
#######################################


class ExecVisualize(QtWidgets.QMainWindow):
    """
    可視化処理の実行

    """

    #########################################
    # def __init__(self, parent, execFuncs, lock):
    def __init__(self, parent):  # 2010.01.05 Minakawa 可視化不具合対策
        """
        コンストラクタ
        @param  parent      親クラス
        @retval 無し
        """
        self.retFuncs = []
        # 2010.01.05 Minakawa 可視化不具合対策 -->
        # self.execFuncs = execFuncs
        # self.lock = lock
        self.parent = parent
        self.lock = parent.lock
        # <-- 2010.01.05 Minakawa 可視化不具合対策 End

        # リソースファイルを取得
        super(ExecVisualize, self).__init__(parent)
        # ウィンドウを取得
        self.fm = Ui_visframe()
        if self.fm is None:
            return
        self.fm.setupUi(self)
        self.rd = Rendering(self.fm)

        # Visualizer ツリーの作成と表示
        self.VisTree()

        # 引数入力準備
        self.PrepareArg()
        # ツリー選択イベントの登録
        self.tree.itemSelectionChanged.connect(self.OnSelectTree)

        # ボタンのイベントハンドラ登録
        self.btExec = self.findChild(QtWidgets.QPushButton, u'btExec')
        self.btClose = self.findChild(QtWidgets.QPushButton, u'btClose')
        self.btExec.clicked.connect(self.OnExec)
        self.btClose.clicked.connect(self.OnClose)
        # クローズイベント登録
        # self.fm.Bind(wx.EVT_CLOSE, self.OnClose)

        # ウィンドウを表示
        self.show()

    #########################################
    def VisTree(self):
        """
        Visualizer ツリーの作成と表示
        @param  無し
        @retval 無し
        """
        # リソースからツリーコントロールを取得
        self.tree = self.findChild(QtWidgets.QTreeWidget, u'tree')
        # ファサードモジュールクラスのインスタンスを取得
        fcmd = FacadeModule()
        # 関数登録リストの準備
        self.rgfuncs = []
        # ルートノードを作成
        root = QtWidgets.QTreeWidgetItem()
        root.setText(0, "Visualizer")
        self.tree.addTopLevelItem(root)
        # 関数登録モジュールのインポートとスクリプトファイルの読み込み
        try:
            funcs = fcmd.GetFuncs(VISMODULE)
        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self, ex)
            return
        # サブノードの下へ関数名を登録
        for func in funcs:
            num = len(VISMODULE) + 1
            funcName = func.mfname[num:]
            a_func = QtWidgets.QTreeWidgetItem(root)
            a_func.setText(0, funcName)
            # リストに追加
            self.rgfuncs.append(func)

        # Functions のサブノードを展開
        self.tree.expandItem(root)

    #########################################
    def PrepareArg(self):
        """
        引数設定関連のコントロール取得
        @param  無し
        @retval 無し
        """
        # 引数文字列とテキストボックスのコントロールを取得
        argst1 = self.findChild(QtWidgets.QLabel, u"argst1")
        argsc1 = self.findChild(QtWidgets.QComboBox, u"argsc1")
        argtx1 = self.findChild(QtWidgets.QLineEdit, u"arg1")
        argst2 = self.findChild(QtWidgets.QLabel, u"argst2")
        argsc2 = self.findChild(QtWidgets.QComboBox, u"argsc2")
        argtx2 = self.findChild(QtWidgets.QLineEdit, u"arg2")
        argst3 = self.findChild(QtWidgets.QLabel, u"argst3")
        argsc3 = self.findChild(QtWidgets.QComboBox, u"argsc3")
        argtx3 = self.findChild(QtWidgets.QLineEdit, u"arg3")
        argst4 = self.findChild(QtWidgets.QLabel, u"argst4")
        argsc4 = self.findChild(QtWidgets.QComboBox, u"argsc4")
        argtx4 = self.findChild(QtWidgets.QLineEdit, u"arg4")
        argst5 = self.findChild(QtWidgets.QLabel, u"argst5")
        argsc5 = self.findChild(QtWidgets.QComboBox, u"argsc5")
        argtx5 = self.findChild(QtWidgets.QLineEdit, u"arg5")
        argst6 = self.findChild(QtWidgets.QLabel, u"argst6")
        argsc6 = self.findChild(QtWidgets.QComboBox, u"argsc6")
        argtx6 = self.findChild(QtWidgets.QLineEdit, u"arg6")
        argst7 = self.findChild(QtWidgets.QLabel, u"argst7")
        argsc7 = self.findChild(QtWidgets.QComboBox, u"argsc7")
        argtx7 = self.findChild(QtWidgets.QLineEdit, u"arg7")
        argst8 = self.findChild(QtWidgets.QLabel, u"argst8")
        argsc8 = self.findChild(QtWidgets.QComboBox, u"argsc8")
        argtx8 = self.findChild(QtWidgets.QLineEdit, u"arg8")
        # 引数入力のラベルとテキストボックスを登録
        self.argList = [(argst1, argtx1, argsc1),
                        (argst2, argtx2, argsc2),
                        (argst3, argtx3, argsc3),
                        (argst4, argtx4, argsc4),
                        (argst5, argtx5, argsc5),
                        (argst6, argtx6, argsc6),
                        (argst7, argtx7, argsc7),
                        (argst8, argtx8, argsc8)]

        # 戻り値テキストボックスのコントロールを取得
        self.rettxt = self.findChild(QtWidgets.QLineEdit, u'retval')

    #############################################
    def OnSelectTree(self, evt=None):
        """
        ツリー選択イベント処理
        @param  evt イベント情報
        @retval 無し
        """
        # 選択された文字列を取得
        text = self.tree.currentItem().text(0)
        mfname = VISMODULE + '.' + text

        # 該当する関数をリストから探す
        args = None
        for func in self.rgfuncs:
            # 関数名称か
            if func.mfname == mfname:
                args = func.args
                break
        # 登録関数でなければ
        if args is None:
            return

        # 引数をテキストボックスへ展開
        i = 0
        for arg in args:
            # 文字列とテキストボックス,コンボボックスのコントロールを取得
            strArg, txtArg, cmbArg = self.argList[i]
            # ラベルを変更
            strArg.setText(arg.label + ": ")
            argitem = arg.value
            if arg.type == "string":
                argitem = argitem.strip('"')
                argitem = argitem.strip("'")

            # 引数の型に応じた処理
            cmbArg.clear()
            # Visualize関数の戻り値を使用する引数か（この場合でもarg.type = "retval"である）
            if arg.value == "(vis_retval)":
                cmbArgList = []
                # 戻り値のラベル抜き出し
                for ret_func in self.retFuncs:
                    if ret_func.retn is not None:
                        if ret_func.retn.label is not None:
                            cmbArgList.append(str(ret_func.retn.label))
                        else:
                            print("#[inamura 241023] ret_func.retn.label is None")
                    else:
                        print("#[inamura 241023] ret_func.retn is None")
                # 抜き出したラベルからコンボボックス作成
                if len(cmbArgList) != 0:
                    for a_label in cmbArgList:
                        cmbArg.addItem(a_label)
                # コンボボックスを有効, テキストボックスを無効
                cmbArg.setEnabled(True)
                txtArg.setEnabled(False)
            # Sequence実行時の戻り値使用か
            elif arg.type == "retval":
                # この際、paramの記述に[Def:XXXX]が含まれているとXXXXの部分が"arg.value"に入るので
                # この値に"(TTTT)"が含まれていればその中身を取り出して戻り値のタイプを決める
                # このタイプを用いてSequenceの戻り値のどれを引数として使用するかを選別する
                listIsArgList = [] # リストオブジェクトに対応か
                listIsArgType = [] # 戻り値のタイプ(ElementContainer, -Array, -Matrix)
                listIsArgInEla = [] # 非弾性データか(VisualContQ)
                # paramの記述に[Def:XXXX,YYYY]が含まれていたなら(arg.value="XXXX,YYYY")
                if arg.value != "":
                    # XXXX,YYYYを取り出す
                    left_bra_id = arg.value.find("(")
                    right_bra_id = arg.value.find(")")
                    if left_bra_id >= 0 and left_bra_id < right_bra_id:
                        inst_s = arg.value[(left_bra_id + 1):right_bra_id]
                        # XXXXとYYYYに分解
                        inst_list = inst_s.split(",")
                        for inst in inst_list:
                            inst = inst.strip()
                            if inst == "ElementContainer":
                                listIsArgList.append(False)
                                listIsArgType.append(Manyo.ElementContainer)
                                listIsArgInEla.append(False)
                            elif inst == "ElementContainerArray":
                                listIsArgList.append(False)
                                listIsArgType.append(Manyo.ElementContainerArray)
                                listIsArgInEla.append(False)
                            elif inst == "ElementContainerMatrix":
                                listIsArgList.append(False)
                                listIsArgType.append(Manyo.ElementContainerMatrix)
                                listIsArgInEla.append(False)
                            elif inst == "ListOfEC":
                                listIsArgList.append(True)
                                listIsArgType.append(Manyo.ElementContainer)
                                listIsArgInEla.append(False)
                            elif inst == "ListOfECA":
                                listIsArgList.append(True)
                                listIsArgType.append(Manyo.ElementContainerArray)
                                listIsArgInEla.append(False)
                            elif inst == "ListOfECM":
                                listIsArgList.append(True)
                                listIsArgType.append(Manyo.ElementContainerMatrix)
                                listIsArgInEla.append(False)
                            elif inst == "ElementContainerMatrixInEla":
                                listIsArgList.append(False)
                                listIsArgType.append(Manyo.ElementContainerMatrix)
                                listIsArgInEla.append(True)
                            else:
                                pass
                # 引数として使用するSequence戻り値のラベル抜き出し
                cmbArgList = []
                for seq_func in self.parent.lineFuncs:
                    # Sequenceの実行関数から戻り値を抽出
                    if seq_func.retn is None:
                        continue
                    a_label = seq_func.retn.label
                    if a_label is None or a_label == "":
                        continue
                    isArgTypeOk = False
                    retResult = seq_func.retn.retResult
                    # 戻り値があれば
                    if retResult is not None:
                        # もしVisualModuleで引数のタイプが書かれていない場合、どの型でもOK
                        if len(listIsArgList) == 0:
                            isArgTypeOk = True
                        # 引数として使用できる戻り値かどうか確認
                        for isArgList, isArgType, isInEla in zip(listIsArgList, listIsArgType, listIsArgInEla):
                            # リストオブジェクトなら中身がタイプに合致するか
                            if isArgList:
                                if isinstance(retResult, list) and len(retResult) > 0:
                                    if isinstance(retResult[0], isArgType):
                                        if isInEla:
                                            if retResult[0].PutHeader().CheckKey(mu.UTSUSEMI_KEY_HEAD_EI) == 1 or retResult[0].PutPointer(0).PutPointer(0).PutHeader().CheckKey(mu.UTSUSEMI_KEY_HEAD_EF) == 1:
                                                isArgTypeOk = True
                                                break
                                        else:
                                            isArgTypeOk = True
                                            break
                            else:
                                # タイプが合致するか
                                if isinstance(retResult, isArgType):
                                    # 非弾性か(VisualContQ専用)
                                    if isInEla:
                                        # VisualContQ用なので戻り値のヘッダに"Ei"があり、かつ"SampleType"が"Powder"ではないことが必要
                                        hh = retResult.PutHeader()
                                        hh2 = retResult.PutPointer(0).PutPointer(0).PutHeader()
                                        if hh.CheckKey(mu.UTSUSEMI_KEY_HEAD_EI) == 1 or hh2.CheckKey(mu.UTSUSEMI_KEY_HEAD_EF) == 1:
                                            # ヘッダに"SampleType"がなくてもOK
                                            if hh.CheckKey(mu.UTSUSEMI_KEY_HEAD_SAMPLETYPE) == 0:
                                                isArgTypeOk = True
                                            elif hh.PutString(mu.UTSUSEMI_KEY_HEAD_SAMPLETYPE) != mu.UTSUSEMI_KEY_HEAD_SAMPLETYPE_POWDER:
                                                isArgTypeOk = True
                                                break
                                    else:
                                        # VisualCont用でなければOK
                                        isArgTypeOk = True
                                        break
                    # 合致するならラベルを保存
                    if isArgTypeOk:
                        a_label = str(a_label)
                        if not (a_label in cmbArgList):
                            cmbArgList.append(a_label)
                cmbArgList.sort()
                # 抜き出したラベルからコンボボックス作成
                for a_label in cmbArgList:
                    cmbArg.addItem(a_label)
                # コンボボックスを有効, テキストボックスを無効
                cmbArg.setEnabled(True)
                txtArg.setEnabled(False)
                # 引数の型指定がなかった場合はテキストボックスも有効に
                if len(listIsArgList) == 0:
                    txtArg.setEnabled(True)
            else:
                # それ以外なら通常処理：コンボボックスを無効, テキストボックスを有効
                txtArg.setText(arg.value)
                cmbArg.setEnabled(False)
                txtArg.setEnabled(True)

            # ツールチップを設定
            if arg.tip.strip() == "":
                pass
            else:
                txtArg.setToolTip(u'%s' % (arg.tip.strip()))

            i += 1

        # 残りの引数テキストボックスをデフォルトに戻す
        while i < 8:
            strArg, txtArg, cmbArg = self.argList[i]
            # ラベルをデフォルトに戻す
            strArg.setText(u"Argument")
            # 値をクリア
            txtArg.setText("")
            cmbArg.clear()
            # 選択不可とする
            txtArg.setEnabled(False)
            cmbArg.setEnabled(False)
            # ツールチップをクリア
            txtArg.setToolTip("")

            i += 1

        # 戻り値無しか
        if func.retn is None:
            self.rettxt.setText("")
            self.rettxt.setEnabled(False)
            self.rettxt.setToolTip("")
        else:
            self.rettxt.setText(u'%s' % (func.retn.label))
            self.rettxt.setEnabled(True)
            if func.retn.tip.strip() == "":
                pass
            else:
                self.rettxt.setToolTip(func.retn.tip)

        # Exec ボタンを選択可とする
        self.btExec.setEnabled(True)

        self.func = func.Copy()

    #############################################
    def OnExec(self):
        """
        関数をメインスレッドで実行する
        引数として、サブスレッドの戻り値を利用可
        @param  evt イベント情報
        @retval 無し
        """
        retFuncs = []
        self.lock.acquire()
        # サブスレッドで実行済み関数の戻り値を取得
        # for efunc in self.execFuncs:
        for efunc in self.parent.lineFuncs:  # 2010.01.05 Minakawa 可視化不具合対策
            # 実行結果があれば
            if efunc.retn is not None and efunc.retn.retResult is not None:
                # リストに入れる
                retFuncs.append(efunc)
        self.lock.release()
        # サブスレッドの結果に、可視化処理の結果を追加
        retFuncs.extend(self.retFuncs)

        # テキストボックスから引数を取得
        i = 0
        execargs = []
        for arg in self.func.args:
            # テキストボックスのコントロールを取得
            txtArg = self.argList[i][1]
            cmbArg = self.argList[i][2]
            # 該当するテキストボックスの内容を取得
            if cmbArg.isEnabled() and (not txtArg.isEnabled()):
                item = cmbArg.currentText().strip()
            elif (not cmbArg.isEnabled()) and txtArg.isEnabled():
                item = txtArg.text().strip()
            elif txtArg.text().strip() == "":
                item = cmbArg.currentText().strip()
            elif txtArg.text().strip() != "":
                item = txtArg.text().strip()
            else:
                print("## Warning for developments: ComboBox and LineEdit objects are mismutched.")
                return
            # 入力値の型をチェックし、OKであれば読み込み
            if not self.rd.CheckArgType(arg, item):
                return

            # 実行形式の引数に変換
            execarg = self.rd.ChangeArg(arg, retFuncs)

            # 正しく変換されていなければ
            if execarg is None:
                try:
                    # 引数の型が戻り値か
                    if arg.type == "retval":
                        # まだ戻り値ができていない
                        raise UtilQt.PlotException(
                            'Sequencer', 'S000', (arg.value,))
                except UtilQt.PlotException as ex:
                    UtilQt.PlotMessage(self, ex)
                # エラー中止
                return
            execargs.append(execarg)
            i += 1

        # 戻り値があるなら
        if self.func.retn is not None:
            # 戻り値を取得
            retlabel = self.rettxt.text().strip()
            try:
                if retlabel == "":
                    raise UtilQt.PlotException(
                        'Common', 'C013', ("Return Label",))
            except UtilQt.PlotException as ex:
                UtilQt.PlotMessage(self, ex)
                # エラー中止
                return

        # 関数の実行
        try:
            try:
                ret = self.func.fobj(*execargs)
            except:
                # 実行時エラー
                raise UtilQt.PlotException('Sequencer', 'S001', ())
        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self, ex)
            return

        # 戻り値があるなら
        if self.func.retn is not None:
            # 戻り値をセット
            self.func.retn.label = retlabel
            self.func.retn.retResult = ret
            # 可視化処理の結果リストに追加
            self.retFuncs.append(self.func)

        # These commands are most important role to remove memory leak
        # delete returned object
        del ret
        # delete objects using as arguments for executing commands
        for ee in execargs:
            del ee

    #############################################
    def OnClose(self):
        """
        画面を閉じる
        @param  None
        @retval 無し
        """
        # ウィンドウを非表示にする
        self.hide()

    #############################################
    def ShowFrame(self):
        """
        画面を閉じる
        @param  無し
        @retval 無し
        """
        # ウィンドウを非表示にする
        self.show()


#######################################
#  CommentDialog
#######################################
class CommentDialog(QtWidgets.QDialog):
    """
    コメント入力ダイアログ
    """
    #########################################

    def __init__(self, parent, comment, cbFunc):
        """
        コンストラクタ
        @param  parent  親ウィンドウ(mainframe)のID
        @param  comment コメントの初期値
        @param  cbFunc  コールバック関数
        @retval 無し
        """
        self.cbFunc = cbFunc
        # リソースファイルを取得
        super(CommentDialog, self).__init__(parent)
        # ウィンドウを取得
        self.dg = Ui_Dialog()
        if self.dg is None:
            return
        self.dg.setupUi(self)

        # テキストボックスのコントロールを取得
        self.txt = self.findChild(QtWidgets.QTextEdit, u'comment')
        # 行毎に分解
        txtlst = comment.split("\n")
        txt = ""
        # 余分な空白、空白行等を除いて再構築

        for tline in txtlst:
            tline = tline.strip()
            if tline:
                txt = txt + tline + "\n"
        # マルチラインテキストボックスに表示
        cursor = self.txt.textCursor()
        cursor.insertText(txt)

        # ボタンのイベントハンドラ登録
        self.btOk = self.findChild(QtWidgets.QPushButton, u'btOk')
        self.btCancel = self.findChild(QtWidgets.QPushButton, u'btCancel')
        self.btOk.clicked.connect(self.OnOK)
        self.btCancel.clicked.connect(self.OnCancel)

        # ダイアログを表示
        self.show()

    #########################################
    def OnOK(self):
        """
        OK ボタン押下イベント処理
        @evt　　イベント
        @retval 無し
        """
        # コメント取得
        comment = self.txt.toPlainText()
        if comment is None:
            print("Comment = None")
        # コールバック関数の実行
        self.cbFunc(*(comment, ))
        # 画面を閉じる
        self.close()
        return

    #########################################
    def OnCancel(self):
        """
        Cansel ボタン押下イベント処理
        @evt　　イベント
        @retval 無し
        """
        # self.dg.EndModal(self.dg.GetReturnCode()) #[inamura 160229]
        # self.dg.Destroy()
        self.close()

#######################################
#  FacadeModule
#######################################


class FacadeModule(object):
    """
    ファサードモジュールクラス
    """
    #########################################

    def GetTip(self, doc, argName):
        """
        引数の説明を取得
        @param  doc  　　関数のドキュメント
        @param  argName  引数の名称
        @retval 説明
        """
        tip = ""
        # 1行づつに分解
        docLines = doc.split('\n')
        for line in docLines:
            # 引数の説明か
            if "@param" in line:
                alst = line.split()
                # 指定された引数か

                if alst[1] == argName:
                    i = 2
                    # 説明を再構築

                    while i < len(alst):
                        tip = tip + alst[i] + " "
                        i += 1

        return tip

    #########################################
    def RenderRet(self, doc):
        """
        ドキュメント中の戻り値を解釈する
        @param  doc 　関数ドキュメント
        @retval 戻り値
        """
        ret = None
        # 1行づつに分解
        docLines = doc.split('\n')
        for line in docLines:
            # 戻り値か
            if "@retval" in line:
                # 単語に分割
                words = line.split()
                # retval の名称が指定されているか
                if len(words) > 1:
                    retN = words[1].strip()
                    n = line.find(retN)

                    # 戻り値が指定されているか
                    if retN != "None":
                        ret = ReturnValue()
                        ret.label = retN
                        ret.tip = line[(n + len(retN)):]
                break
        return ret

    #########################################
    def GetFuncDoc(self, fp):
        """
        関数ドキュメントを取得
        @param  fp 　ファイルポインタ
        @retval 関数ドキュメント
        """
        newDoc = ""
        # 1行読み込み(def ステートメントの次の行)
        line = fp.readline()

        # 関数ドキュメントか　
        if line and line.find('"""') > 0:
            # 先頭の空白文字と行末の改行コードを削除
            line = line.strip()
            line = line.rstrip()
            # トリプルクォーテーションの後に文字列があるか
            if len(line) > 3:
                newDoc = newDoc + line[3:] + "\n"
            # 次のトリプルクォーテーションまで読み込む
            while 1:
                line = fp.readline()
                # 後ろのトリプルクォーテーションの前にファイルが終了する場合のプロテクト
                # インポートできないはずだが、念のため
                if not line:
                    return ""

                # 空白、改行コードを除く
                line = line.strip()

                # ドキュメントの終了か
                if line.find('"""') >= 0:
                    # トリプルクォーテーションの前に文字列があるか
                    if line.find('"""') != 0:
                        # トリプルクォーテーションを除いて、ドキュメントに追加
                        newDoc = newDoc + line[:-3]
                    break
                else:
                    newDoc = newDoc + line + "\n"

        return newDoc

    #########################################
    def EvaluateDic(self, line, fp):
        """
        関数登録モジュールの整合性チェック
        @param  line     キーワード　'_funcs' を含む行
        @param  fp       モジュールファイルのポインタ
        @retval True: OK  False:NG
        """
        n = line.find('{')
        line = line[(n + 1):].strip()
        retVal = True
        contFlag = True
        # ディクショナリが終わるまで
        while contFlag:
            # キー(関数名称)が取得できるまで
            while contFlag:
                strFunc = ""
                objFunc = ""
                n = line.find(':')
                if n >= 0:
                    strFunc = line[:n].strip()
                    line = line[(n + 1):].strip()
                    # 値(関数オブジェクト)が取得できるまで
                    while contFlag:
                        n = line.find(',')
                        if n >= 0:
                            if objFunc == "":
                                objFunc = line[:n].strip()
                            line = line[(n + 1):]
                            break
                        elif len(line) > 0:
                            # ディクショナリの終了か
                            n = line.find('}')
                            if n == 0:
                                contFlag = False
                                break
                            # 関数オブジェクトありか
                            elif n > 1:
                                objFunc = line[:n].strip()
                                contFlag = False
                                break
                            else:
                                objFunc = line
                        line = fp.readline().strip()

                elif '}' in line and not (':' in line):
                    contFlag = False
                    break
                line = fp.readline()
                # 関数名称取得済みか

                if strFunc != "":
                    # クォーテーションコードを除去
                    strFunc = strFunc.strip('"')
                    strFunc = strFunc.strip("'")
                    # 関数名称とオブジェクトが不一致であれば
                    if strFunc != objFunc:
                        retVal = False
                        contFlag = False

        return retVal

    #########################################
    def RenderModules(self, module, mdfuncs, mdfuncs_order):
        """
        [inamura 140323]
        Render modules of facade fuction
        @param module   name of mudule
        @param mdfuncs  function dictionary of module
        @param mdfuncs_order
        @retval function list
        """
        lstFuncs = []
        import inspect
        mdfunc_keys = list(mdfuncs.keys())
        mdfunc_lists = []
        if mdfuncs_order is None:
            mdfunc_keys.sort()
            for func_name in mdfunc_keys:
                mdfunc_lists.append(func_name)
        else:
            for func_name in mdfuncs_order:
                if func_name in mdfunc_keys:
                    mdfunc_lists.append(func_name)

        for mdfunc in mdfunc_lists:
            func = Function()
            func.mfname = module.strip() + "." + mdfunc
            defmod = __import__(module.strip())
            deffunc = getattr(defmod, mdfunc)
            # print "SequenceEditor2 >> import ",func.mfname
            if platform.python_version_tuple()[0] == '2':
                modargs = inspect.getargspec(deffunc)
            else:
                modargs = inspect.getfullargspec(deffunc)
            v_argnames = modargs.args
            v_argdefs = modargs.defaults

            func.fobj = mdfuncs[mdfunc]
            func.fdoc = deffunc.__doc__

            if len(v_argnames) == 0:
                func.args = []
            elif v_argdefs is None:
                print("mdfunc,v_argnames,v_argdefs=",
                      mdfunc, v_argnames, v_argdefs)
                continue
            else:
                func.args = []
                for arg_name, arg_def in zip(v_argnames, v_argdefs):
                    arg = Argument()
                    arg.label = arg_name
                    arg.value = str(arg_def)
                    # print '### arg.label, arg.value=',arg_name,arg_def
                    # print "type(arg.label)=",type(arg.label)
                    # print "type(arg.value)=",type(arg.value)
                    if arg.label == "HISTORY":
                        arg.value = "HISTORY"
                        arg.type = "retval"
                    elif arg_def is None:
                        arg.value = self.RenderDefault(func.fdoc, arg.label)
                        arg.type = "retval"
                    elif isinstance(arg_def, bool):
                        arg.type = "boolean"
                    elif isinstance(arg_def, str):
                        arg.type = "string"
                    elif isinstance(arg_def, float):
                        arg.type = "float"
                    elif isinstance(arg_def, int):
                        arg.type = "integer"
                    else:

                        try:
                            float(arg.value)
                        except:

                            arg.type = "retval"
                        else:

                            if '.' in arg.value:

                                arg.type = "float"
                            else:
                                arg.type = "integer"

                    arg.tip = self.GetTip(func.fdoc, arg.label)
                    # 引数を登録
                    func.args.append(arg)

            func.retn = self.RenderRet(func.fdoc)

            lstFuncs.append(func)

        return lstFuncs
    #########################################

    def RenderDefault(self, doc, label):
        """
        [inamura 140323]
        Analysis default value for an argument from doc
        @param  doc   function document
        @param  label argument label
        @retval default value(string)
        """
        ret = None
        # 1行づつに分解
        docLines = doc.split('\n')
        for line in docLines:
            # 戻り値か
            if "@param" in line:
                # 単語に分割
                words = line.split()
                for a_word in words:
                    if a_word == label:
                        left_bra = line.find("[Def:")
                        if left_bra > 0:
                            right_bra = line.find("]", left_bra)
                            if right_bra > 0:
                                ret = line[(left_bra + 5):right_bra]
                                break
        if ret is None:
            ret = ""
        return ret

    #########################################
    def RenderFile(self, module, fp, mdfuncs):
        """
        ファサードモジュールファイルから関数文字列の行を取得
        @param  module  モジュール名称
        @param  fp       モジュールファイルのポインタ
        @param  mdfuncs  モジュールに登録されている関数のディクショナリ
        @retval 無し
        """
        # 関数登録用リストを準備
        lstFuncs = []
        # 1行づつ読み込む
        while 1:
            try:
                line = fp.readline()
            except:
                break

            # ファイルの終わりか
            if not line:
                break
            # 関数か
            lw = line.split()
            if len(lw) > 0 and lw[0] == "def":
                # モジュールに登録されている関数中にあるか
                # 関数の名称が読み込んだ行の中にあるか
                nx = line.find("def") + 3
                n0 = line.find('(')
                n1 = line.rfind(')')

                if nx > 0 and nx < n0 and n0 < n1:

                    for funcName in mdfuncs:

                        if line[nx:n0].strip() == funcName:

                            func = Function()
                            # モジュール名付きで、関数名を登録
                            func.mfname = module + '.' + funcName
                            # 関数のアドレスを取得

                            func.fobj = mdfuncs[funcName]
                            # 関数のドキュメントを取得

                            # (2バイトコードが含まれる可能性があるため、ここでは __doc__  は使用しない)
                            func.fdoc = self.GetFuncDoc(fp)
                            # 引数の文字列を取得
                            strarg = line[(n0 + 1):n1].strip()
                            # 引数無しか
                            if strarg == "":
                                func.args = []
                            else:
                                lst = strarg.split(',')
                                # 文字列リストを解釈して引数リストを作成
                                func.args = self.RenderArg(func.fdoc, lst)

                            # 関数ドキュメントより、戻り値を取得
                            func.retn = self.RenderRet(func.fdoc)

                            # 登録関数情報を作成
                            lstFuncs.append(func)

            # 関数登録ディクショナリか
            # elif "_functions" in line:
            elif "_functions" in line[:10]:  # [tito 120518]
                # 関数登録ディクショナリの評価
                if not self.EvaluateDic(line, fp):
                    raise UtilQt.PlotException('Sequencer', 'S014', (module,))

        # 読み込み終了
        fp.close()

        return lstFuncs

    #########################################
    def RenderArg(self, doc, list):
        """
        文字列リストを解釈して引数リストを作成
        @param  doc   関数ドキュメント
        @param  list  文字列リスト
        @retval 引数リスト
        """
        # 引数のリストを準備
        argsReal = []

        # 引数を登録
        for item in list:
            arg = Argument()
            # デフォルト値が指定されているか
            if '=' in item:
                strarg, defa = item.split('=')
                arg.label = strarg.strip()

                # 余分な空白を取り除き、デフォルト値とする
                arg.value = defa.strip()
                # 履歴を示す特殊引数か
                if arg.label == "HISTORY":
                    arg.value = "HISTORY"
                    arg.type = "retval"
                # 文字列かどうかを判定
                elif arg.value[0] == '"' or arg.value[0] == "'":
                    # 文字列の前後の引用符を取り除く
                    arg.value = arg.value.strip("'")
                    arg.value = arg.value.strip('"')
                    # 属性を文字列に設定
                    arg.type = "string"
                    # ブーリアン変数か
                elif arg.value == "True" or arg.value == "False":
                    arg.type = "boolean"
                else:
                    # 数値か
                    try:
                        float(arg.value)
                    except:
                        # 文字列、ブーリアン、数値以外は戻り値と仮定
                        arg.type = "retval"
                    else:
                        # 小数点があれば
                        if '.' in arg.value:
                            # 実数とする
                            arg.type = "float"
                        else:
                            arg.type = "integer"

            # デフォルト値の指定が無い場合
            else:
                if "HISTORY" in item:
                    arg.label = "HISTORY"
                    arg.type = "retval"
                    arg.value = "HISTORY"
                else:
                    arg.value = ""
                    arg.type = "retval"
                    arg.label = item.strip()

            # 説明を取得
            arg.tip = self.GetTip(doc, arg.label)
            # 引数を登録
            argsReal.append(arg)

        return argsReal

    #########################################
    def GetFuncs(self, module):
        """
        スクリプトを開き、登録されている
        関数を解析して関数のリストを返す
        @param  module スクリプトのモジュール名
        @retval 登録関数リスト
        """

        # スクリプトファイル名を作る
        pyfile = module + ".py"

        fullPath = UtilQt.AcqPath().GetModulePath(pyfile)
        # print(fullPath)

        if fullPath is None:

            # 環境変数PYTHONPATHに指定されたディレクトリを取得
            paths = str(os.environ.get('PYTHONPATH')).split(os.pathsep)
            # print(paths)
            # 全PYTHONPATHディレクトリを探索
            for path in paths:
                fpath = os.path.normpath(os.path.join(path, pyfile))

                # ディレクトリに目的ファイルがあるか
                if os.path.isfile(fpath):
                    # フルパスを確定
                    fullPath = fpath
                    break
        # 指定されたファイルが、Python Path 中に無ければ
        if fullPath is None:
            # 空白を入力していない場合

            if len(module.strip()) > 0:
                raise UtilQt.PlotException('Sequencer', 'S009', (module,))
                return
            else:
                return None

        else:
            mdfuncs = {}
            # モジュールをインポートし、関数ディクショナリと、モジュールの属性を取得
            try:
                # 文字列を、インポート用の変数に変換してインポート
                mod = __import__(module)
            except:
                # インポート不可のメッセージ表示
                raise UtilQt.PlotException('Sequencer', 'S008', (module,))
            try:
                # 関数登録ディクショナリを取得
                mdfuncs = mod._functions
            except:
                # 関数登録用ディクショナリが無い
                raise UtilQt.PlotException('Sequencer', 'S010', (module,))

            # 登録されている関数が無い場合は
            mdfuncs_order = None
            try:
                mdfuncs_order = mod._functionsOrder
            except:
                pass
            """
            if len(mdfuncs) == 0:
                return []
            # スクリプトファイルを開く
            try:
                fp = codecs.open(fullPath, 'r', "utf-8")
            except:
                # ファイルが開けない
                raise UtilQt.PlotException('Common', 'C006', (fullPath,))
            """
            # スクリプトファイルを解析し、登録されている関数のリストを作って返す
            try:
                funcs = self.RenderModules(
                    module, mdfuncs, mdfuncs_order)
            except:
                # 解析中に何かエラーがあった
                raise

            return funcs

    #########################################
    def GetSingleFunc(self, a_mod_func):
        """
        Find given module and return imported module
        @param  module name for module
        @retval imported module
        """
        tmp_func = a_mod_func.split(".")
        if len(tmp_func) <= 1:
            return None
        module = tmp_func[0]
        method = tmp_func[1]
        # スクリプトファイル名を作る
        pyfile = module + ".py"

        fullPath = UtilQt.AcqPath().GetModulePath(pyfile)

        if fullPath is None:
            # 環境変数PYTHONPATHに指定されたディレクトリを取得
            paths = str(os.environ.get('PYTHONPATH')).split(os.pathsep)
            # 全PYTHONPATHディレクトリを探索
            for path in paths:
                fpath = os.path.normpath(os.path.join(path, pyfile))

                # ディレクトリに目的ファイルがあるか
                if os.path.isfile(fpath):
                    # フルパスを確定
                    fullPath = fpath
                    break
        # 指定されたファイルが、Python Path 中に無ければ
        if fullPath is None:
            # 空白を入力していない場合
            if len(module.strip()) > 0:
                raise UtilQt.PlotException('Sequencer', 'S009', (a_mod_func,))
                return
            else:
                return None
        else:
            mdfuncs = {}
            # モジュールをインポートし、関数ディクショナリと、モジュールの属性を取得
            try:
                # 文字列を、インポート用の変数に変換してインポート
                mod = __import__(module)
            except:
                # インポート不可のメッセージ表示
                raise UtilQt.PlotException('Sequencer', 'S008', (module,))
            try:
                # 関数登録ディクショナリを取得
                mdfuncs = mod._functions
            except:
                # 関数登録用ディクショナリが無い
                raise UtilQt.PlotException('Sequencer', 'S010', (module,))

            # スクリプトファイルを解析し、登録されている関数のリストを作って返す
            try:
                funcs = self.RenderModules(module, mdfuncs, [method])
            except:
                # 解析中に何かエラーがあった
                raise
            if len(funcs) > 0:
                return funcs[0]
            else:
                raise


#######################################
#  SequenceEditor
#######################################
class SequenceEditor(QtWidgets.QMainWindow):
    """
    解析シーケンサのメインフレーム
    """
    # シーケンスの実行状態定義
    # [inamura 120403]-->
    STATUS = {-1: "Inexecutable",
              0: "Executable",
              1: "Executing Command(s)",
              2: "Waiting to Stop (Editable)",
              3: "Waiting to Stop (Not Editable)",
              4: "Resuming from Stop"}
    # <--[inamura 120403]

    #########################################
    def __init__(self, modules, initFilePath=None):
        """
        メインフレームのコンストラクタ
        @param  modules  インポート対象のモジュールのリスト
        @param initFilePath
        @retval 無し
        """
        # 初期化
        super(SequenceEditor, self).__init__(None)
        self.modules = modules
        self.vis = None
        self.statNo = -1
        self.activeFnc = None
        self.lineFuncs = []
        """
        # 測定シーケンスか
        if modules[0] == '0':
            # 起動元から渡された認証情報を設定
            AH.SetAuthenticInfo(modules[1], modules[2])
            modules = modules[3:]
            self.acqF = True
        else:
            self.acqF = False
        """
        self.acqF = False

        # シーケンス排他制御用のロックを作る
        self.lock = _thread.allocate_lock()

        # 画面作成
        self.frame = Ui_MainWindow()
        if self.frame is None:
            return
        # self.frame.SetIcon(UtilPlot.Images().GetMainIcon())
        self.frame.setupUi(self)

        if self.acqF:
            self.frameTitle = "Measuring Sequence"
        else:
            # タイトル文字列を取得
            self.frameTitle = self.windowTitle()

        # タイトルに、ファイル名を付加(最初は New)
        self.setWindowTitle(self.frameTitle + " - New")

        # 各クラスのインスタンスを取得
        self.execC = ExecSequence(self.lock)
        self.sfile = SequenceFile(self)
        self.rd = Rendering(self)

        # 各種コントロールの取得とイベントハンドラのセット
        self.SetControls()

        # リストコントロールにイメージリストを登録
        self.statusImage = [None, None, None, None, None]
        self.statusImage[0] = UtilQt.ImagesXpm().GetQPixmap("Red")
        self.statusImage[1] = UtilQt.ImagesXpm().GetQPixmap("Green")
        self.statusImage[2] = UtilQt.ImagesXpm().GetQPixmap("Processing")
        self.statusImage[3] = UtilQt.ImagesXpm().GetQPixmap("GreenEnd")
        self.statusImage[4] = UtilQt.ImagesXpm().GetQPixmap("RedEnd")

        # リストのタイトル行を表示
        self.list1.setColumnCount(3)
        self.list1.setHeaderLabels(["Sequence", "Begin Time", "End Time"])

        # カラムの幅を設定
        self.list1.setColumnWidth(0, 640)
        self.list1.setColumnWidth(1, 80)
        self.list1.setColumnWidth(2, 80)

        # Set Tree widget for commands ( to add horizontal scroll bar )
        self.tree.setColumnCount(1)
        self.tree.setColumnWidth(0, 400)

        # 登録されている関数の一覧をツリー表示
        if self.modules[0][0] == "STRUCTURED":
            self.rgfuncs = FunctionTree(self.frame).MakeStructuredNode(
                self.tree, "SIK", modules)
        else:
            self.rgfuncs = FunctionTree(self.frame).MakeNode(
                self.tree, "Functions", modules)

        # 画面表示を初期化
        self.ChangeBtnStatus()
        # メインフレーム画面の表示
        self.show()

        self.LoadInitSequence(initFilePath)

        self.LogFile = None
        if not mu.UtsusemiEnvGetDebugMode():
            pass
        else:
            dt = datetime.datetime.now()
            tmp_LogFile = BASELOGFILE % (
                dt.strftime("%Y%m%d%H%M%S"), os.getpid())
            try:
                tmp_LogFile = os.path.join(
                    os.environ["UTSUSEMI_USR_DIR"], "ana", "tmp", tmp_LogFile)
                ff = open(tmp_LogFile, "w")
                ff.write("[%s] SequenceEditor start\n" %
                         (dt.strftime("%Y%m%d%H%M%S")))
                ff.close()
                self.LogFile = tmp_LogFile
            except:
                print("### Failed making LogFile as " + tmp_LogFile)
                self.LogFile = None
        self.execC.SetLogFileName(self.LogFile)

        self.currentDirTxt.setText(os.getcwd())

        # Set Current Dir
        work_dir = mu.UtsusemiEnvGetWorkDir()
        if work_dir != "":
            os.chdir(work_dir)
            self.currentDirTxt.setText(work_dir)
        else:
            self.currentDirTxt.setText(os.getcwd())


    #########################################
    def SetControls(self):
        """
        各種イコントロールの取得とベントハンドラの登録
        @param  無し
        @retval 無し
        """
        # リソースからツリーコントロールとリストコントロールを取得
        self.tree = self.findChild(QtWidgets.QTreeWidget, u'tree0')
        self.list1 = self.findChild(QtWidgets.QTreeWidget, u'list1')
        self.list1.setSelectionMode(
            QtWidgets.QAbstractItemView.ExtendedSelection)

        # 予約語入力テキストボックスのコントロール取得
        """
        panel = self.res.GetCtrl(self.frame, 'panel')
        runtx1 = self.res.GetCtrl(panel, 'runno1')
        runtx2 = self.res.GetCtrl(panel, 'runno2')
        runtx3 = self.res.GetCtrl(panel, 'runno3')
        runtx4 = self.res.GetCtrl(panel, 'runno4')
        deftx1 = self.res.GetCtrl(panel, 'deftx1')
        deftx2 = self.res.GetCtrl(panel, 'deftx2')
        deftx3 = self.res.GetCtrl(panel, 'deftx3')
        deftx4 = self.res.GetCtrl(panel, 'deftx4')
        """
        # 測定シーケンスなら
        """
        if self.acqF:
            # Run No. を選択不可とする
            runtx1.Enable(False)
            runtx2.Enable(False)
            runtx3.Enable(False)
            runtx4.Enable(False)
        """
        # ディクショナリへ設定
        """
        self.reserved = {"RUN1":runtx1,
                         "RUN2":runtx2,
                         "RUN3":runtx3,
                         "RUN4":runtx4,
                         "DEF1":deftx1,
                         "DEF2":deftx2,
                         "DEF3":deftx3,
                         "DEF4":deftx4}
        """
        self.reserved = {}
        splash_path = os.path.join(
            os.environ['UTSUSEMI_BASE_DIR'], 'ana/Reduction/', 'LOGO_utsusemi4_c_banner.png')
        if os.path.exists(splash_path):
            splash_panel = self.findChild(QtWidgets.QLabel, u'SplashPanel')
            splash_panel.setAlignment(QtCore.Qt.AlignCenter)
            splash_panel.setPixmap(QtGui.QPixmap(splash_path))

        # 関数ドキュメント表示用テキストボックスのコントロールを取得
        self.fdocTxt = self.findChild(QtWidgets.QTextBrowser, u'funcdoc')
        self.fdocTxt.setLineWrapColumnOrWidth(800)
        self.fdocTxt.setLineWrapMode(QtWidgets.QTextEdit.FixedPixelWidth)

        # 引数文字列とテキストボックスのコントロールを取得
        argst1 = self.findChild(QtWidgets.QLabel, u"argst1")
        argtx1 = self.findChild(QtWidgets.QLineEdit, u"arg1")
        argst2 = self.findChild(QtWidgets.QLabel, u"argst2")
        argtx2 = self.findChild(QtWidgets.QLineEdit, u"arg2")
        argst3 = self.findChild(QtWidgets.QLabel, u"argst3")
        argtx3 = self.findChild(QtWidgets.QLineEdit, u"arg3")
        argst4 = self.findChild(QtWidgets.QLabel, u"argst4")
        argtx4 = self.findChild(QtWidgets.QLineEdit, u"arg4")
        argst5 = self.findChild(QtWidgets.QLabel, u"argst5")
        argtx5 = self.findChild(QtWidgets.QLineEdit, u"arg5")
        argst6 = self.findChild(QtWidgets.QLabel, u"argst6")
        argtx6 = self.findChild(QtWidgets.QLineEdit, u"arg6")
        argst7 = self.findChild(QtWidgets.QLabel, u"argst7")
        argtx7 = self.findChild(QtWidgets.QLineEdit, u"arg7")
        argst8 = self.findChild(QtWidgets.QLabel, u"argst8")
        argtx8 = self.findChild(QtWidgets.QLineEdit, u"arg8")
        argst9 = self.findChild(QtWidgets.QLabel, u"argst9")
        argtx9 = self.findChild(QtWidgets.QLineEdit, u"arg9")
        argst10 = self.findChild(QtWidgets.QLabel, u"argst10")
        argtx10 = self.findChild(QtWidgets.QLineEdit, u"arg10")
        argst11 = self.findChild(QtWidgets.QLabel, u"argst11")
        argtx11 = self.findChild(QtWidgets.QLineEdit, u"arg11")
        argst12 = self.findChild(QtWidgets.QLabel, u"argst12")
        argtx12 = self.findChild(QtWidgets.QLineEdit, u"arg12")
        argst13 = self.findChild(QtWidgets.QLabel, u"argst13")
        argtx13 = self.findChild(QtWidgets.QLineEdit, u"arg13")
        argst14 = self.findChild(QtWidgets.QLabel, u"argst14")
        argtx14 = self.findChild(QtWidgets.QLineEdit, u"arg14")
        argst15 = self.findChild(QtWidgets.QLabel, u"argst15")
        argtx15 = self.findChild(QtWidgets.QLineEdit, u"arg15")

        self.rettxt = self.findChild(QtWidgets.QLineEdit, u'retval')

        # 引数入力のラベルとテキストボックスを登録
        self.argList = [(argst1, argtx1),
                        (argst2, argtx2),
                        (argst3, argtx3),
                        (argst4, argtx4),
                        (argst5, argtx5),
                        (argst6, argtx6),
                        (argst7, argtx7),
                        (argst8, argtx8),
                        (argst9, argtx9),
                        (argst10, argtx10),
                        (argst11, argtx11),
                        (argst12, argtx12),
                        (argst13, argtx13),
                        (argst14, argtx14),
                        (argst15, argtx15)]

        # ステータス表示ラベルのコントロールを取得
        self.statTxt = self.findChild(QtWidgets.QLabel, u'status')
        self.currentDirTxt = self.findChild(QtWidgets.QLabel, u'stCurrentDir')

        # ボタンのコントロールを取得
        self.btBegin = self.findChild(QtWidgets.QPushButton, u'btBegin')
        self.btStop = self.findChild(QtWidgets.QPushButton, u'btStop')
        self.btResume = self.findChild(QtWidgets.QPushButton, u'btResume')
        self.btOpen = self.findChild(QtWidgets.QPushButton, u'btOpen')
        self.btClear = self.findChild(QtWidgets.QPushButton, u'btClear')
        self.btAdd = self.findChild(QtWidgets.QPushButton, u'btAdd')
        self.btSaveas = self.findChild(QtWidgets.QPushButton, u'btSaveas')
        self.btUp = self.findChild(QtWidgets.QPushButton, u'btUp')
        self.btDown = self.findChild(QtWidgets.QPushButton, u'btDown')
        self.btCopyAdd = self.findChild(QtWidgets.QPushButton, u'btCopyAdd')
        self.btCopyAdd.setEnabled(False)
        self.btCCD = self.findChild(QtWidgets.QPushButton, u'btCCD')
        self.btRemove = self.findChild(QtWidgets.QPushButton, u'btRemove')
        self.btSet = self.findChild(QtWidgets.QPushButton, u'btSet')
        self.btVis = self.findChild(QtWidgets.QPushButton, u'btVis')

        btAdd_icon = self.btAdd.style().standardIcon(QtWidgets.QStyle.SP_ArrowRight)
        btRemove_icon = self.btRemove.style().standardIcon(QtWidgets.QStyle.SP_TrashIcon)
        btOpen_icon = self.btOpen.style().standardIcon(
            QtWidgets.QStyle.SP_DialogOpenButton)
        btSaveas_icon = self.btSaveas.style().standardIcon(
            QtWidgets.QStyle.SP_DialogSaveButton)
        btUp_icon = self.btUp.style().standardIcon(QtWidgets.QStyle.SP_ArrowUp)
        btDown_icon = self.btDown.style().standardIcon(QtWidgets.QStyle.SP_ArrowDown)
        btCopyAdd_icon = self.btCopyAdd.style().standardIcon(
            QtWidgets.QStyle.SP_BrowserReload)
        self.btAdd.setIcon(btAdd_icon)
        self.btRemove.setIcon(btRemove_icon)
        self.btOpen.setIcon(btOpen_icon)
        self.btSaveas.setIcon(btSaveas_icon)
        self.btUp.setIcon(btUp_icon)
        self.btDown.setIcon(btDown_icon)
        self.btCopyAdd.setIcon(btCopyAdd_icon)

        # 実行中に選択不可とするボタンのコントロールを取得
        self.bttns = [self.btAdd,
                      self.btRemove,
                      self.btOpen,
                      self.btSaveas,
                      self.btClear,
                      self.btSet,
                      self.btUp,
                      self.btDown,
                      self.btCopyAdd]

        # ボタンのイベントハンドラ登録
        self.btAdd.clicked.connect(self.OnAdd)
        self.btRemove.clicked.connect(self.OnRemove)
        self.btBegin.clicked.connect(self.OnBegin)
        self.btStop.clicked.connect(self.OnStop)
        self.btResume.clicked.connect(self.OnResume)
        self.btOpen.clicked.connect(self.OnOpen)
        self.btVis.clicked.connect(self.OnVisualize)
        self.btSaveas.clicked.connect(self.OnSaveas)
        self.btClear.clicked.connect(self.OnClear)
        self.btSet.clicked.connect(self.OnSet)
        self.btUp.clicked.connect(self.OnUp)
        self.btDown.clicked.connect(self.OnDown)
        self.btCopyAdd.clicked.connect(self.OnCopyAdd)
        self.btCCD.clicked.connect(self.OnChangeCurrentDir)

        # リスト選択時のイベントハンドラ登録
        self.list1.itemSelectionChanged.connect(self.OnSelect)
        self.LIST1_UNSELECTED = -1
        self.LIST1_EXECUTED = -999999
        self.list1_selected = self.LIST1_UNSELECTED

        # ツリー選択イベントの登録
        self.tree.itemSelectionChanged.connect(self.OnSelectTree)

        # クローズイベント登録
        # self.frame.Bind(wx.EVT_CLOSE, self.OnClose)

    #########################################
    def ChangeStepsStatus(self):
        """
        実行可能であれば、Begin ボタンを選択可とする
        @param  無し
        @retval 無し
        """

        # リストの行数を取得
        nLine = self.list1.topLevelItemCount()

        lstRet = [EMPTYDAT]
        bgflag = True
        # 登録されている全行を調べる
        for i in range(nLine):

            # 関数データを作成
            func = self.lineFuncs[i]
            flag = True

            for arg in func.args:
                # 先行関数の戻り値を引数としてとるか
                if arg.type == "retval":
                    if arg.label != "HISTORY":
                        # 先行する関数の戻り値として指定されていなければ
                        if not (arg.value in lstRet):
                            flag = False
                # その他の引数で、値がなければ
                elif arg.value == "" or arg.value == '""':
                    flag = False
            # 引数エラー無しか
            if flag:

                # 結果無しであれば
                if func.stat <= 0:
                    # 緑信号イメージを表示
                    self.list1.topLevelItem(i).setIcon(0, self.statusImage[1])
                    self.list1.topLevelItem(i).setText(1, "")
                    self.list1.topLevelItem(i).setText(2, "")
                    func.stat = 0
            # 引数エラーがあれば
            else:

                # 赤信号イメージを表示
                self.list1.topLevelItem(i).setIcon(0, self.statusImage[0])
                self.list1.topLevelItem(i).setText(1, "")
                self.list1.topLevelItem(i).setText(2, "")
                func.ClearResult()
                func.stat = -1
                bgflag = False

            # 戻り値があれば
            if func.retn is not None:
                # 戻り値を登録
                lstRet.append(func.retn.label)
        try:
            # Waiting to Resume 中であれば
            if self.statNo == 4:
                # 要求解除のワーニングを表示
                raise UtilQt.PlotException('Sequencer', 'S002', ())
        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self, ex)

        # Resumption Bookable または Resumption Booked であれば
        if self.statNo >= 2:
            if bgflag:
                # 状態変更(→ Resumption Bookable)
                self.statNo = 2
            else:
                # 状態変更(→ Resumption NOT Bookable)
                self.statNo = 3
        # Ready または Not Ready であれば
        else:
            if bgflag and self.list1.topLevelItemCount():
                # 状態変更(→ Ready)
                self.statNo = 0
            else:
                # 状態変更(→ Not Ready)
                self.statNo = -1

        # 状態変更後の処理
        self.ChangeBtnStatus()

    #########################################
    def OnApply(self, evt=None):
        """
        Apply ボタン押下イベント処理
        予約語テキストボックスを実行関数リストに適用する
        @param evt　　イベント
        @retval 無し
        """
        self.lock.acquire()
        # シーケンスを調べる
        i = 0
        for func in self.lineFuncs:
            # 予約語を、テキストボックスから取得した値に置き換える
            if self.CheckReserved(func.args):
                # 変更したステップの影響が及ぶステップの結果をクリア
                self.rd.CheckBackwash(i, self.lineFuncs)
                # 変更した関数の結果をクリア
                func.ClearResult()
            # 表示行を作る
            strLine = self.rd.MakeLine(func)
            # 表示を置き換え
            self.list1.topLevelItem(i).setText(0, strLine)
            i += 1

        # Begin ボタンの選択可/不可を設定
        self.ChangeStepsStatus()
        # 選択行の内容を、テキストボックスへ反映
        self.OnSelect()
        self.list1_selected = self.LIST1_UNSELECTED
        self.lock.release()

    #########################################
    def OnAdd(self, evt=None):
        """
        Add ボタン押下イベント処理
        関数を実行リストに追加
        @param evt　　イベント
        @retval 無し
        """
        # 関数が選択されていなければ何もしない
        if self.activeFnc is None:
            return
        # 引数と戻り値をデフォルトに戻す
        func = self.activeFnc.Copy()
        self.lock.acquire()
        self.lineFuncs.append(func)

        # リストに1行追加、テキストボックスに値を設定
        self.SetTxt(func)
        # Begin ボタンの選択可/不可を設定
        self.list1_selected = self.LIST1_UNSELECTED
        self.ChangeStepsStatus()
        self.lock.release()

    #########################################
    def SetTxt(self, func):
        """
        テキストボックスに値を設定
        予約語をテキストボックスの内容に置き換える
        @param func　　関数データ
        @retval 無し
        """
        # リストの行数を取得
        nLine = self.list1.topLevelItemCount()
        # 予約語を、テキストボックスから取得した値に置き換える
        self.CheckReserved(func.args)

        # 表示する文字列を作成
        strLine = self.rd.MakeLine(func)
        # リストに1行を追加
        new_line = QtWidgets.QTreeWidgetItem()
        new_line.setText(0, strLine)
        self.list1.addTopLevelItem(new_line)
        # 先に選択されていた行を非選択状態にする
        index = self.list1.currentItem()
        # if index is not None:
        if isinstance(index, QtWidgets.QTreeWidgetItem):
            self.list1.selectionModel().clearSelection()

        # 追加した行を選択状態とする
        self.list1.setCurrentItem(self.list1.topLevelItem(nLine))
        # self.list1_selected = self.LIST1_UNSELECTED

    #########################################
    def CheckReserved(self, args):
        """
        引数が予約語であれば、テキストボックス
        から値を取得
        @param args　　引数リスト
        @retval True: 置き換えあり、False: 置き換え無し
        """
        retval = False
        for arg in args:
            # 予約語として登録されているか
            try:
                txtcont = self.reserved[arg.label]
            except:
                pass
            else:
                # 該当するテキストボックスの値を取得
                value = txtcont.text().strip()
                # テキストボックスが空ではなく、前と異なっていれば
                if value != "" and arg.value != value:
                    # 引数のタイプを判定し、OKであれば入れ替え
                    retval = self.rd.CheckArgType(arg, value)

        return retval

    ##########################################
    def OnSelect(self, evt=None):
        """
        リスト選択イベントハンドラ
        @param  evt イベント情報
        @retval 無し
        """
        self.btUp.setEnabled(False)
        self.btDown.setEnabled(False)
        self.btCopyAdd.setEnabled(False)
        self.btRemove.setEnabled(False)
        # 選択されている行を取得
        item = self.list1.currentItem()
        index = self.list1.indexOfTopLevelItem(item)
        i = self.list1.topLevelItemCount()

        if index < 0:
            self.repaint()
            return

        if self.list1_selected == index or self.list1_selected == self.LIST1_EXECUTED:
            self.OnDeselectedList()
            self.list1_selected = self.LIST1_UNSELECTED
            self.repaint()
            return
        else:
            self.list1_selected = index

        if self.statNo <= 0:  # "Inexecutable","Executable"を表示中のとき
            if index != 0:  # 選択行が最上部の行でなければ
                self.btUp.setEnabled(True)
            if index != (i - 1):  # 選択行が最下部の行でなければ
                self.btDown.setEnabled(True)

        if (self.statNo != 1) and (self.statNo != 4):
            self.btCopyAdd.setEnabled(True)
            self.btRemove.setEnabled(True)

        if (self.statNo == 2) or (self.statNo == 3):  # not executing
            if self.lineFuncs[index].stat <= 0:  # 選択コマンドが未実行で
                # 選択行が最上部ではなく、一つ前のコマンドが未実行なら
                if (index != 0) and (self.lineFuncs[(index - 1)].stat == 0):
                    self.btUp.setEnabled(True)
                # 選択行が最終行でなければ
                if index != (i - 1):
                    self.btDown.setEnabled(True)

        # 選択行の関数を取得
        func = self.lineFuncs[index]
        i = 0
        for arg in func.args:
            # 文字列とテキストボックスのコントロールを取得
            strArg, txtArg = self.argList[i]

            if arg.label != "HISTORY":

                # ラベルを変更
                strArg.setText(arg.label + ": ")
                argitem = arg.value
                if arg.type == "string":
                    argitem = argitem.strip('"')
                    argitem = argitem.strip("'")

                txtArg.setText(argitem)

                if (self.statNo == 1) or (self.statNo == 4):
                    txtArg.setEnabled(False)
                else:
                    # 選択可とする
                    txtArg.setEnabled(True)

                # ツールチップを設定
                if arg.tip.strip() == "":
                    pass
                else:
                    txtArg.setToolTip(arg.tip.strip())
                i += 1
                # 引数の数が8個を超えるか
                try:
                    if i > len(self.argList):
                        raise UtilQt.PlotException(
                            'Sequencer', 'S007', (func.mfname,))
                except UtilQt.PlotException as ex:
                    UtilQt.PlotMessage(self, ex)
                    break

        # 残りの引数テキストボックスをデフォルトに戻す
        while i < len(self.argList):
            strArg, txtArg = self.argList[i]
            # ラベルをデフォルトに戻す
            strArg.setText("Argument:")
            # 値をクリア
            txtArg.setText("")
            # 選択不可とする
            txtArg.setEnabled(False)
            # ツールチップをクリア
            txtArg.setToolTip("")
            i += 1

        # 戻り値を設定
        if func.retn is None:
            self.rettxt.setText("")
            self.rettxt.setEnabled(False)
            self.rettxt.setToolTip("")
        else:
            self.rettxt.setText(func.retn.label)

            if (self.statNo == 1) or (self.statNo == 4):
                self.rettxt.setEnabled(False)
            else:
                self.rettxt.setEnabled(True)

            if func.retn.tip.strip() == "":
                pass
            else:
                self.rettxt.setToolTip(func.retn.tip)

        # [inamura 120404]-->
        """
        try:
            # Resumtion Booked 中であれば
            if self.statNo == 4:
                # Resumtion Bookable へ戻す
                self.statNo = 2
                self.ChangeBtnStatus()
                # 予約解除のワーニングを表示
                raise UtilPlot.PlotException('Sequencer', 'S002', ())
        except UtilPlot.PlotException, ex:
            UtilPlot.PlotMessage(self.frame, ex)
        """
        # <--[inamura 120404]
        self.repaint()
        return
    ##########################################

    def OnSelectTree(self, evt=None):
        """
        ツリー選択イベントハンドラ
        @param  evt イベント情報
        @retval 無し
        """
        # 選択された文字列を取得
        text = self.tree.currentItem().text(0)
        # 関数一覧から検索
        for func in self.rgfuncs:
            # 関数名称か
            if func.mfname == text:
                self.activeFnc = func
                # 関数ドキュメントをテキストボックスへ表示
                self.fdocTxt.setPlainText(func.fdoc)
                # ツールチップを設定
                if func.fdoc.strip() == "":
                    self.fdocTxt.setToolTip("")
                else:
                    self.fdocTxt.setToolTip(func.fdoc)
                return
        # 関数ではない
        self.activeFnc = None
        # 関数ドキュメントをクリア
        self.fdocTxt.clear()

    #########################################

    def OnRemove(self, evt=None):
        """
        Remove ボタン押下イベント処理
        選択関数を実行リストから削除
        @param evt　　イベント
        @retval 無し
        """
        # リストが空でなければ
        if self.list1.topLevelItemCount() > 0:
            selected_items = self.list1.selectedItems()

            if len(selected_items) > 0:
                self.lock.acquire()
                index_list = []
                for an_item in selected_items:
                    index_list.append(self.list1.indexOfTopLevelItem(an_item))
                self.list1.selectionModel().clearSelection()

                index_list.sort()
                num_of_removed = 0
                for an_index in index_list:
                    index = an_index - num_of_removed
                    # if under executing
                    try:
                        if self.lineFuncs[index].stat == 1:
                            raise UtilQt.PlotException('Sequencer', 'S012', ())
                        elif (self.statNo == 2) and (self.lineFuncs[index].stat == 2):
                            continue
                        else:
                            self.rd.CheckBackwash(index, self.lineFuncs)
                            # 選択行を削除
                            self.list1.takeTopLevelItem(index)
                            self.lineFuncs[index].ClearResult()
                            del self.lineFuncs[index]
                            # Begin ボタンの選択可/不可を設定
                            self.ChangeStepsStatus()
                            self.OnClearAllArgs()
                            num_of_removed += 1

                    except UtilQt.PlotException as ex:
                        self.lock.release()
                        UtilQt.PlotMessage(self, ex)

                self.lock.release()

        self.list1_selected = self.LIST1_UNSELECTED

    #########################################
    def OnBegin(self, evt=None):
        """
        Exec ボタン押下イベント処理
        関数リストを実行
        @param evt　　イベント
        @retval 無し
        """

        # 関数リストの最初から実行実行
        self.DoStart(0)

    #########################################
    def DoStart(self, index):
        """
        実行開始準備処理
        @param index 開始行
        @retval 無し
        """
        # 状態変更(→　In Process)
        self.statNo = 1
        self.ChangeBtnStatus()
        # 選択状態を消す
        self.list1.selectionModel().clearSelection()
        self.list1_selected = self.LIST1_EXECUTED
        self.OnClearAllArgs()

        # 開始行の後の実行結果を削除
        i = index
        while i < len(self.lineFuncs):
            self.lineFuncs[i].ClearResult()
            i += 1

        # 関数リストの実行を開始
        self.execC.OnStart(index, self.lineFuncs)

        # 実行状態を監視するために、0.5秒毎のタイマーをセット
        self.timer = QtCore.QTimer(self)
        self.timer.timeout.connect(self.OnTimer)
        self.timer.start(500)

    #############################################
    def OnTimer(self, evt=None):
        """
        タイマールーチン
        @param  evt イベント情報
        @retval 無し
        """

        self.lock.acquire()
        lnum = self.list1.topLevelItemCount()
        # 開始時間と終了時間を表示
        for i in range(lnum):
            # 実行開始時間
            self.list1.topLevelItem(i).setText(1, self.lineFuncs[i].beginTime)
            # 実行終了時間
            self.list1.topLevelItem(i).setText(2, self.lineFuncs[i].endTime)
            # 実行状態イメージ表示
            stNum = self.lineFuncs[i].stat + 1
            # 実行可であれば
            if self.lineFuncs[i].stat >= 0:
                self.list1.topLevelItem(i).setIcon(0, self.statusImage[stNum])

        self.lock.release()
        # エラーがあるか
        if self.execC.erstatus > 0:
            # Aborted 表示
            self.list1.topLevelItem(self.execC.execNum).setText(
                self.execC.erstatus, "Aborted")
            # タイマーを停止
            self.timer.stop()
            # 状態変更(→ Executable)
            self.statNo = 0
            self.ChangeBtnStatus()
            # 実行時エラーの表示
            ret = QtWidgets.QMessageBox().critical(None, u"Run-time Error",
                                                   self.execC.ermessage, QtWidgets.QMessageBox.Ok)
            return

        # シーケンスが停止したか
        if self.execC.status < 0:
            # タイマーを停止
            self.timer.stop()

            # In Process または Resumption Bookable 中か
            if self.statNo == 1 or self.statNo == 2:
                # 状態変更(→ Executable)
                self.statNo = 0
            # Resumption NOT Bookable 中か
            elif self.statNo == 3:
                # 状態変更(→　Inexecutable)
                self.statNo = -1
            # Resumption Booked 中か

            elif self.statNo == 4:
                # 関数リストの実行
                self.DoStart(self.resumeIndex)
            # 状態変更後の処理
            self.ChangeBtnStatus()
            self.OnClearAllArgs()

    #########################################
    def OnStop(self, evt=None):
        """
        Abort ボタン押下イベント処理
        関数リストの実行停止
        @param evt　　イベント
        @retval 無し
        """
        # if "Waiting to Resume", not send Stop to commands
        if self.statNo == 4:
            self.statNo = 2
        else:
            # シーケンスの実行停止要求をだす
            self.execC.OnStop()

        # 状態変更(→　Resumption Bookable)
        self.statNo = 2
        self.ChangeBtnStatus()

    #########################################
    def ChangeBtnStatus(self):
        """
        状態変更対応処理
        状態表示を変更
        ボタン類の選択可/不可を設定
        @param 無し
        @retval 無し
        """
        # 状態文字列を取得し表示
        strStat = self.STATUS[self.statNo]
        self.statTxt.setText(strStat)

        # 実行中か
        if self.statNo == 1:
            # スクリプト編集関連ボタンは選択不可
            for bt in self.bttns:
                bt.setEnabled(False)
            # Stop 以外のボタンは選択不可
            self.btBegin.setEnabled(False)
            self.btResume.setEnabled(False)
            self.btStop.setEnabled(True)
        # 実行中以外
        else:
            if self.statNo == 4:
                for bt in self.bttns:
                    bt.setEnabled(False)
                self.btStop.setEnabled(True)
                self.btResume.setEnabled(False)
                return

            # スクリプト編集関連ボタンは選択可
            for bt in self.bttns:
                bt.setEnabled(True)

            self.btStop.setEnabled(False)

            # Executable なら
            if self.statNo == 0:
                self.btBegin.setEnabled(True)
                self.btSaveas.setEnabled(True)
                self.btResume.setEnabled(False)
                # 正常終了データがあれば、Resume も選択可とする
                for func in self.lineFuncs:
                    # 正常終了データがあるか
                    if func.stat == 2:
                        self.btResume.setEnabled(True)
                        break
            # Inexecutable 、 Not Acceptable または Waiting to Resume なら
            elif self.statNo == -1 or self.statNo >= 3:
                self.btBegin.setEnabled(False)
                self.btResume.setEnabled(False)
                self.btSaveas.setEnabled(False)
            # Waiting to Stop なら
            elif self.statNo == 2:
                self.btBegin.setEnabled(False)
                self.btResume.setEnabled(True)

            # 再開処理時は、Open、Save、 Clear　は選択不可とする
            if self.statNo >= 2:
                self.btOpen.setEnabled(False)
                self.btSaveas.setEnabled(False)
                self.btClear.setEnabled(False)

            # ステップ数が0か
            if self.list1.topLevelItemCount() == 0:
                for bt in self.bttns:
                    bt.setEnabled(False)
                self.btOpen.setEnabled(True)
                self.btAdd.setEnabled(True)
                # タイトルのファイル名をクリア
                self.setWindowTitle(self.frameTitle + " - New")
                # リストのツールチップをクリア
                self.list1.setToolTip("New")
                self.docTip = ""

            else:  # ステップ数が0でないとき
                self.OnSelect()

    #########################################
    def OnResume(self, evt=None):
        """
        Resume ボタン押下イベント処理
        関数リストの実行を再開
        @param evt　　イベント
        @retval 無し
        """
        # 選択中の行を取得
        selected = self.list1.currentItem()
        index = -1
        if isinstance(selected, QtWidgets.QTreeWidgetItem):
            index = self.list1.indexOfTopLevelItem(selected)
        try:
            # 選択行が無ければ
            if index < 0:

                # raise UtilPlot.PlotException('Sequencer', 'S003', ())

                for i in range(len(self.lineFuncs)):
                    if self.lineFuncs[i].stat == 0:
                        index = i
                        break
                    # command under executing?
                    if self.lineFuncs[i].stat == 1:
                        index = i + 1
                        break

            # if selected command is under executing, resume from next command
            elif self.lineFuncs[index].stat == 1:
                index += 1
            else:
                i = 0
                # 再開行の上に、正常終了以外の行がないかをチェック
                while i < index:
                    if self.lineFuncs[i].stat != 2:
                        # 実行中の行か
                        if self.lineFuncs[i].stat != 1:
                            raise UtilQt.PlotException('Sequencer', 'S013', ())
                    i += 1

        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self, ex)
            return

        # Executable 中か
        if self.statNo == 0:
            # 関数リストの実行
            self.DoStart(index)
        # Resumption Bookable: 再開予約可中
        if self.statNo == 2:
            # 状態変更(→　Resumption Booked )
            self.statNo = 4
            self.resumeIndex = index
            # 状態表示変更
            strStat = self.STATUS[self.statNo]
            self.statTxt.setText(strStat)
            self.ChangeBtnStatus()
            self.OnClearAllArgs()

    #########################################
    def OnVisualize(self, evt=None):
        """
        Visualize ボタン押下イベント処理
        Visualize 実行画面を開く
        @param evt　　イベント
        @retval 無し
        """
        # 先に起動されているか
        if self.vis is None:
            # 画面を作成
            self.vis = ExecVisualize(self)  # 2010.01.05 Minakawa
        else:
            # 画面を表示
            self.vis.OnClose()
            self.vis.ShowFrame()

    #########################################

    def OnClear(self):
        """
        Clear ボタン押下イベント処理
        関数リストをクリア
        @param evt　　イベント
        @retval 無し
        """
        # リストをクリア
        self.list1.clear()

        for i in range(len(self.lineFuncs)):
            self.lineFuncs[i].ClearResult()

        self.lineFuncs = []
        # 状態変更
        self.statNo = -1
        self.ChangeBtnStatus()

    #########################################
    def OnOpen(self, argv):
        """
        File Open or Load initial sequence file
        シーケンスファイルを開く
        @param argv arguments
        @retval 無し
        """
        self.OpenSequenceFile()

    #########################################
    def OpenSequenceFile(self, initFile="", defPath=""):
        """
        File Open or Load initial sequence file
        シーケンスファイルを開く
        @param initFile Initial sequence file
        @param defPath default path to open file
        @retval 無し
        """
        # ファイルオープンダイアログを開き、シーケンスを読み込む
        isNeedDlg = True
        if initFile != "":
            isNeedDlg = False
            defPath = ""
        elif defPath == "":
            defPath = os.getcwd()

        try:
            ftpl = self.sfile.OpenSequence(
                self.rgfuncs, self.acqF, isNeedDlg, initFile, defPath)
        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self, ex)
            return
        else:
            # ファイルオープンダイアログでファイルを指定しないとき(Noneが戻り値のとき)
            if ftpl is None:
                return

        try:
            fname, funcs = ftpl
        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self, ex)
            return

        # リストをクリアしない
        # self.list1.DeleteAllItems()
        # self.lineFuncs = funcs
        self.lock.acquire()
        # Ignore event of item-selection-change on List
        self.list1.itemSelectionChanged.disconnect()

        # 1行づつ読み込む
        for func in funcs:
            self.lineFuncs.append(func)
            # リストに1行を追加
            self.SetTxt(func)

        # 1行目を選択状態へ
        ind_last = len(self.lineFuncs) - 1
        self.list1.topLevelItem(ind_last).setSelected(False)
        ind_select = len(self.lineFuncs) - len(funcs)
        self.list1.topLevelItem(ind_select).setSelected(True)
        self.list1.setCurrentItem(self.list1.topLevelItem(ind_select))

        self.list1_selected = self.LIST1_EXECUTED

        # 選択行の内容を、テキストボックスへ反映
        self.OnSelect()

        # Turn on action for the event of item-selection-change on List
        self.list1.itemSelectionChanged.connect(self.OnSelect)

        # タイトルバーとリストのツールチップを更新
        self.UpdateSequence(fname)

        # Begin ボタンの選択可/不可を設定

        self.ChangeStepsStatus()

        self.lock.release()

    #########################################
    def OnSaveas(self):
        """
        Save As ボタン押下イベント処理
        関数実行リストを保存
        @param evt　　イベント
        @retval 無し
        """
        # ファイル保存ダイアログを開き、シーケンスを保存
        try:
            fname = self.sfile.SaveSequence(self.lineFuncs, self.acqF)
        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self, ex)
            return
        # キャンセルされていなければ
        if fname is not None:
            # タイトルバーとリストのツールチップを更新
            self.UpdateSequence(fname)

    #########################################
    def OnChangeCurrentDir(self):
        """
        Current Dir ボタン押下イベント処理
        関数実行リストを保存
        @param evt　　イベント
        @retval 無し
        """
        dirname = QtWidgets.QFileDialog.getExistingDirectory(
            self, caption="Choose Current Directory", dir=os.getcwd())
        if dirname != "":
            os.chdir(dirname)
            self.currentDirTxt.setText(os.getcwd())

    #########################################

    def UpdateSequence(self, fname):
        """
        タイトルバーのシーケンス名とツールチップを更新
        @param  fname　　シーケンスファイル名
        @retval 無し
        """
        # タイトルに、ファイル名を付加
        title = u"%s - %s" % (self.frameTitle, fname)
        self.setWindowTitle(title)
        # ツールチップ文字列を作成
        tip = u"\n  File Name: " + fname + "  \n"
        # 最終更新日時を取得

        # 関数ドキュメントを取得
        ftime = self.sfile.GetFileTime()
        tip = tip + u"  Created or Updated at " + ftime + "  \n\n"

        fDoc = self.sfile.GetFileDoc()
        if fDoc:
            tip = tip + fDoc
        # リストにツールチップを表示
        self.list1.setToolTip(tip)

    #########################################
    def OnSet(self):
        """
        Set ボタン押下イベント処理
        設定された引数と戻り値の変数名を変更
        @param evt　　イベント
        @retval 無し
        """
        # 選択状態の行が無ければ
        selected = self.list1.selectedItems()
        index = -1
        if len(selected) == 1:
            index = self.list1.indexOfTopLevelItem(selected[0])
        try:
            if index < 0:
                raise UtilQt.PlotException('Sequencer', 'S003', ())
        except UtilQt.PlotException as ex:
            UtilQt.PlotMessage(self, ex)
            return

        self.lock.acquire()
        # 当該行の関数データを取得

        func = self.lineFuncs[index]

        i = 0
        strArg = ""
        for arg in func.args:
            # 文字列とテキストボックスのコントロールを取得
            stt, txtArg = self.argList[i]

            if arg.label == "HISTORY":
                item = "HISTORY"
            else:
                # 値を取得
                item = txtArg.text().strip()
                i += 1
            # 入力の型チェック
            if not self.rd.CheckArgType(arg, item):
                self.lock.release()
                return

            # デフォルト値の型が実数のときテキストボックスに実数を表示
            if arg.type == "float":
                item = "%s" % float(item)

                txtArg.setText(item)

        # 戻り値があるか
        if func.retn is not None:
            # 戻り値のラベルを取得して置き換え
            strRet = self.rettxt.text().strip()
            try:
                if strRet == "":
                    raise UtilQt.PlotException(
                        'Common', 'C013', ("Return Label",))
            except UtilQt.PlotException as ex:
                UtilQt.PlotMessage(self, ex)
                return
            # 戻り値がある場合、変更したステップの影響が及ぶステップの結果をクリア
            self.rd.CheckBackwash(index, self.lineFuncs)
            # 戻り値ラベルの入れ替え(影響を評価した後に入れ替えること)
            func.retn.label = strRet
        line = self.rd.MakeLine(func)

        # リストを書き換える
        self.list1.currentItem().setText(0, line)

        # 変更した関数の結果をクリア
        func.ClearResult()
        self.lock.release()
        # Start ボタンの選択可/不可を設定
        self.list1_selected = self.LIST1_UNSELECTED
        self.ChangeStepsStatus()

    #########################################
    # def OnClose(self):
    def eventClose(self, evt):
        """
        クローズイベント処理
        関数リストが実行中かどうかをチェック
        @param  evt　　イベント
        @retval 無し
        """

        try:
            # Executable 中または　Inexecutable 中以外はクローズできない
            if self.statNo > 0:
                evt.ignore()
                raise UtilQt.PlotException('Sequencer', 'S004', ())

        except UtilQt.PlotException as ex:
            evt.ignore()
            UtilQt.PlotMessage(self, ex)
            return

        if self.vis is not None:
            self.vis.close()
        evt.accept()

    ###################################
    def OnUp(self):
        """
        表示順変更ボタン「↑」押下イベント処理
        @param evt イベント
        @retval 無
        """
        index = -1
        selected = self.list1.currentItem()
        if isinstance(selected, QtWidgets.QTreeWidgetItem):
            index = self.list1.indexOfTopLevelItem(selected)
        if index < 0:
            return
        self.lock.acquire()

        selected.setSelected(False)
        # 選択した行とその上の行を入れ替え
        dat = self.lineFuncs[index]
        self.lineFuncs[index] = self.lineFuncs[(index - 1)]
        self.lineFuncs[(index - 1)] = dat
        # 入れ替えて行を表示
        for i in range((index - 1), (index + 1)):
            sLine = self.rd.MakeLine(self.lineFuncs[i])
            self.list1.topLevelItem(i).setText(0, sLine)

        # 入れ替え後、行を選択
        self.list1.setCurrentItem(self.list1.topLevelItem(index - 1))
        self.list1_selected = self.LIST1_UNSELECTED
        self.lock.release()
        # 選択行から下の行は「未実行」
        for i in range((index - 1), len(self.lineFuncs)):
            # 開始・終了時間表示をクリア
            self.lineFuncs[i].stat = 0
            self.list1.topLevelItem(i).setText(1, "")
            self.list1.topLevelItem(i).setText(2, "")
            self.list1.topLevelItem(i).setIcon(0, self.statusImage[1])

        self.ChangeStepsStatus()

    ####################################
    def OnDown(self):
        """
        表示順変更ボタン「↓」押下イベント処理
        @param evt: イベント
        @retval 無
        """

        index = -1
        selected = self.list1.currentItem()
        if isinstance(selected, QtWidgets.QTreeWidgetItem):
            index = self.list1.indexOfTopLevelItem(selected)
        if index < 0:
            return
        self.lock.acquire()
        selected.setSelected(False)

        # 選択した行とその下の行を入れ替え
        dat = self.lineFuncs[index]
        self.lineFuncs[index] = self.lineFuncs[(index + 1)]
        self.lineFuncs[(index + 1)] = dat
        # 入れ替えた行を表示
        for i in range(index, (index + 2)):
            sLine = self.rd.MakeLine(self.lineFuncs[i])
            self.list1.topLevelItem(i).setText(0, sLine)
        # 入れ替えた行を選択
        self.list1.setCurrentItem(self.list1.topLevelItem(index + 1))
        self.list1_selected = self.LIST1_UNSELECTED
        self.lock.release()
        # 入れ替えて選択行より下の行は「未実行」
        for i in range(index, len(self.lineFuncs)):
            # 開始時間・終了時間をクリア
            self.list1.topLevelItem(i).setText(1, "")
            self.list1.topLevelItem(i).setText(2, "")
            self.list1.topLevelItem(i).setIcon(0, self.statusImage[1])

        self.ChangeStepsStatus()

    ####################################
    def OnCopyAdd(self):
        """
        表示順変更ボタン「↓」押下イベント処理
        @param evt: イベント
        @retval 無
        """
        selected_items = self.list1.selectedItems()
        if len(selected_items) == 0:
            return
        index_list = []
        for an_item in selected_items:
            index_list.append(self.list1.indexOfTopLevelItem(an_item))
            an_item.setSelected(False)
        index_list.sort()

        self.lock.acquire()

        for index in index_list:
            # Add Selected command at last of lineFuncs
            func = self.lineFuncs[index].Copy()
            self.lineFuncs.append(func)
            # リストに1行追加、テキストボックスに値を設定
            self.SetTxt(func)

        # Begin ボタンの選択可/不可を設定
        self.list1_selected = self.LIST1_UNSELECTED
        self.ChangeStepsStatus()
        self.lock.release()

    # [inamura 120321]-->
    ####################################
    def LoadInitSequence(self, initFilePath=None):
        """
         イニシャルシークエンス読み込み
         起動時にシークエンスを読み込む
         @param initFilePath
         @retval None
        """
        path = ""
        if initFilePath is None:
            path = os.path.join(
                os.environ["UTSUSEMI_USR_DIR"], "ana", INITSEQFILE)
        else:
            if os.path.exists(initFilePath):
                dname = os.path.dirname(initFilePath)
                if dname != "":
                    os.chdir(dname)
                    self.currentDirTxt.setText(os.getcwd())
                path = os.path.basename(initFilePath)
            else:
                print("There is no given file.(%s)" % initFilePath)

        if not os.path.exists(path):
            if mu.UtsusemiEnvGetDebugMode():
                print("There is no initial sequence file.", path)
            return

        self.OpenSequenceFile(initFile=path, defPath="")
        return

    ####################################
    def OnDeselectedList(self):
        """
        Listの選択が外れた時の処理
        @param evt
        @retval None
        """
        self.OnClearAllArgs()
        self.btRemove.setEnabled(False)
        self.btCopyAdd.setEnabled(False)
        self.btUp.setEnabled(False)
        self.btDown.setEnabled(False)

    ####################################
    def OnClearAllArgs(self):
        """
         引数欄をクリアする
         @param None
         @retval None
        """
        # テキストボックスをデフォルトに
        for strArg, txtArg in self.argList:
            # ラベルをデフォルトに戻す
            strArg.setText("Argument:")
            # 値をクリア
            txtArg.setText("")
            # 選択不可とする
            txtArg.setEnabled(False)
            # ツールチップをクリア
            txtArg.setToolTip("")
        # 戻り値をクリア
        self.rettxt.setText("")
        self.rettxt.setEnabled(False)
        self.rettxt.setToolTip("")


#######################################
#  Sequencer
#######################################
class Sequencer(object):
    """
    Application Class
    """
    #########################################

    def __init__(self, modules=None, initFilePath=None):
        """
        アプリケーションスタート
        @param  modules  ファサードモジュールのリスト
                指定が無い場合は、デフォルトのモジュール
        @retval 無
        """
        # 測定シーケンスか
        if modules is not None and modules[0] == '0':
            pass
        else:
            try:
                Code = mu.UtsusemiEnvGetInstCode()
            except:
                print("Error!: No environment settings.")
                return
            # 指定無しか
            if modules is None:
                try:
                    # import Sequencer_facades
                    # COMMON = (Code, Sequencer_facades.__AnaFacades__)
                    # VISUALIZER = ("Visualizer",Sequencer_facades.__VisFacades__)
                    import glob
                    ana_facades = []
                    vis_facades = []
                    facade_struct = []
                    for pypath in sys.path:
                        fpath = glob.glob(os.path.join(pypath, BASEFACADEFILE))
                        for a_fpath in fpath:
                            path_to_dir, module_file = os.path.split(a_fpath)
                            isBaseCommand = False
                            if path_to_dir.find(Code) >= 0:
                                isBaseCommand = True

                            module_name = module_file[:-3]
                            mod_tmp = __import__(module_name)
                            try:
                                # if there is user-defined structure, overwight it.
                                if len(facade_struct) != 0:
                                    if pypath.find(os.path.join(mu.UtsusemiEnvGetUserDir())) >= 0:
                                        facade_struct = mod_tmp.__Structures__
                                else:
                                    facade_struct = mod_tmp.__Structures__
                            except:
                                pass
                            try:
                                for ana_mod in mod_tmp.__AnaFacades__:
                                    if not (ana_mod in ana_facades):
                                        if isBaseCommand:
                                            ana_facades.insert(0, ana_mod)
                                        else:
                                            ana_facades.append(ana_mod)
                            except:
                                pass
                            try:
                                for vis_mod in mod_tmp.__VisFacades__:
                                    if not (vis_mod in vis_facades):
                                        if isBaseCommand:
                                            vis_facades.insert(0, vis_mod)
                                        else:
                                            vis_facades.append(vis_mod)
                            except:
                                pass
                    if len(ana_facades) != 0:
                        COMMON = (Code, ana_facades)
                    else:
                        COMMON = None
                    if len(vis_facades) != 0:
                        VISUALIZER = ("Visualizer", vis_facades)
                    else:
                        VISUALIZER = None
                    if len(facade_struct) != 0:
                        STRUCTURED = ("STRUCTURED", facade_struct)
                    else:
                        STRUCTURED = None

                except:
                    # デフォルトモジュール
                    COMMON = (Code, ["Cmm", "Com"])
                    VISUALIZER = ("Visualizer", ["D1Plotter"])
                    STRUCTURED = None
            else:
                mod_list_st = modules
                enable_mod_list = []
                for mod_st in mod_list_st:
                    try:
                        tmp = __import__(mod_st)
                        enable_mod_list.append(mod_st)
                    except:
                        print("########## Error on Sequence Editor")
                        print(
                            "########## Could not import module = {}".format(mod_st))
                        continue
                COMMON = (Code, enable_mod_list)
                VISUALIZER = ("Visualizer", ["D1Plotter"])
                STRUCTURED = None

            modules = []
            if STRUCTURED is not None:
                modules.append(STRUCTURED)
            if COMMON is not None:
                modules.append(COMMON)
            if VISUALIZER is not None:
                modules.append(VISUALIZER)

        # Hi-resolution Display for windows
        if PYSIDEVER != 6:
            if hasattr(QtCore.Qt, 'AA_EnableHighDpiScaling'):
                QtWidgets.QApplication.setAttribute(
                    QtCore.Qt.AA_EnableHighDpiScaling, True)
        app = QtWidgets.QApplication(sys.argv)
        # Set Default Font Size to (default 14)
        deffont = app.font()
        if "UTSUSEMI_DEFAULT_FONTSIZE" in os.environ:
            deffontsize = int(os.environ["UTSUSEMI_DEFAULT_FONTSIZE"])
            deffont.setPointSize(deffontsize)
        else:
            deffont.setPointSize(14)
        app.setFont(deffont)
        app.setWindowIcon(QtGui.QIcon(os.path.join(
            os.environ["UTSUSEMI_BASE_DIR"], "ana", "Reduction", "ICON_Utsusemi4.png")))

        splash_path = os.path.join(
            os.environ["UTSUSEMI_BASE_DIR"], "ana", "Reduction", "LOGO_utsusemi4_c_box.png")
        self.splash = None
        if os.path.exists(splash_path):
            pixmap = QtGui.QPixmap(splash_path)
            self.splash = QtWidgets.QSplashScreen(
                pixmap, QtCore.Qt.WindowStaysOnTopHint)
            self.splash.setMask(pixmap.mask())
            self.splash.show()
            self.splash.showMessage(u"Now Launching ...")
            app.processEvents()
            import time
            time.sleep(2)

        seq = SequenceEditor(modules, initFilePath)
        if self.splash is not None:
            self.splash.finish(seq)
        if PYSIDEVER == 6:
            sys.exit(app.exec())
        else:
            sys.exit(app.exec_())


if __name__ == '__main__':

    # 引数が指定されていれば置き換え
    if len(sys.argv) > 1:
        mdls = []
        i = 1
        initFile = None
        while(i < len(sys.argv)):
            if sys.argv[i].find("-file=") == 0:
                initFile = sys.argv[i][6:]
                if initFile == "":
                    initFile = None
            else:
                mdls.append(sys.argv[i])
            i += 1
        if len(mdls) == 0:
            mdls = None
        Sequencer(mdls, initFile)
    else:
        Sequencer()
