#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
DetectorMap.py
検出器のマップ表示
"""
from __future__ import print_function
import glob
import time
import os
from numpy import arange, array, meshgrid, zeros, float64
import copy
import Manyo.Utsusemi as mu
import Manyo
import Manyo.MLF as mm
import math
import uGao.FastPlotQt as FastPlot
import uGao.uGaoUtil as uGaoUtils
import uGao.M2PlotPlus as D2Vis
import threading
from matplotlib import cm
from matplotlib.figure import Figure
from matplotlib.colors import LogNorm
import pylab
import sys
import matplotlib

PYSIDEVER = 1
try:
    from PySide6 import QtCore, QtGui, QtWidgets
    from PySide6.QtGui import QAction
    PYSIDEVER = 6
except:
    try:
        from PySide2 import QtCore, QtGui, QtWidgets
        from PySide2.QtWidgets import QAction
        PYSIDEVER = 2
    except:
        from PySide import QtCore, QtGui
        import PySide.QtGui as QtWidgets
        from PySide.QtGui import QAction

# 先に指定されていなかったら、バックエンドを指定
if QtCore.__version__ < '5.0.0':
    if not matplotlib.get_backend() == 'Qt4Agg':
        matplotlib.use('Qt4Agg')
        matplotlib.rcParams['backend.qt4'] = 'PySide'
    from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigCanvas
    from utsusemi.vis.ui_DetectMapQ import Ui_mapframe
else:
    from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigCanvas
    if sys.platform == "darwin":
        if not matplotlib.get_backend() == 'Qt5Agg':
            matplotlib.use('Qt5Agg')
    if PYSIDEVER == 2:
        from utsusemi.vis.ui2_DetectMapQ import Ui_mapframe
    elif PYSIDEVER == 6:
        from utsusemi.vis.ui6_DetectMapQ import Ui_mapframe

NAVIGATIONTOOLBAR_CHANGED_VER = '2.0'
matplotlib_ver = matplotlib.__version__
vers = matplotlib_ver.split('.')
matplotlib_verNo = int(vers[0]) * 1000 + int(vers[1]) * 10 + int(vers[2])


try:
    import numpy.ma as ma
except:
    import numpy.core.ma as ma


try:
    import scipy.weave as weave
    import platform
    wflag = True and (platform.system() != "Windows")
except:
    wflag = False


if mu.UtsusemiEnvGetDebugMode():
    mu.UtsusemiMessage("DetectorMap PySide Ver={}".format(PYSIDEVER))

MASKVALUE = mu.UTSUSEMIMASKVALUE64
# History
# [200120] First release of PySide + Python3 version
# print("#[inamura 220408] DetectorMap head fin.")

#######################################
#  DMatrixObj
#######################################


class DMatrixObj(object):

    """
    エレメントコンテナマトリックスのハンドリングクラス
    """

    #########################################
    def __init__(self, parent, matrix, hTable, isPixelReverse=False):
        """
        コンストラクタ
        @param  parent    親クラスのインスタンス
        @param  matrix  　　 エレメントコンテナマトリックス
        @param  hTable　      PSD読み出し用のハッシュテーブル
        @retval 無し
        """
        self.matrix = matrix
        self.hTable = hTable
        self.numBank = parent.numBank
        self.numPSD = parent.numPSD
        self.numPixel = parent.numPixel
        self.frame = parent.frame

        # データ変換クラスのインスタンスを取得
        self.ce = uGaoUtils.ConvertEC()

        # 無効検出器ID 格納用のリストを準備
        self.masked = []

        self.isPixelReverse = isPixelReverse

    #########################################
    def ReadMatrix(self, func):
        """
        エレメントコンテナマトリックスを読み込み、カウントマップを作成
        @param  func　     読み込み終了時のコールバック関数
        @retval 無し
        """
        self.progress = 0
        numRead = len(self.hTable)
        # プログレスバーダイアログ表示
        pdg = QtWidgets.QProgressDialog("Loading Matrix", None, 0, numRead, None,
                                        QtCore.Qt.Window | QtCore.Qt.WindowTitleHint | QtCore.Qt.CustomizeWindowHint)
        # サブスレッドスタート(データ読み込み)
        th = threading.Thread(target=self._ReadData)
        th.daemon = True
        th.start()

        # 進捗表示
        import time
        while self.progress < numRead:
            time.sleep(1)
            pdg.setValue(self.progress)

        pdg.destroy()

        # コールバック関数の実行(マップ表示)
        func()

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

    def _ReadData(self):
        """
        エレメントコンテナマトリックスを読み出す
        @param   matrix  エレメントコンテナマトリックス
        @param  hTable　 PSD読み出し用のハッシュテーブル
        @retval 無し
        """
        # データマップ格納用アレイを準備
        if mu.UtsusemiEnvGetDebugMode():
            mu.UtsusemiMessage("self.numBank, self.numPSD ={}, {}".format(
                self.numBank, self.numPSD))
            mu.UtsusemiMessage("len(self.hTable)={}".format(len(self.hTable)))
        iPSD = self.numBank * (self.numPSD + 2) - 2
        iPSD = len(self.hTable)
        # mu.UtsusemiMessage("iPSD={}".format(iPSD))
        self.arData = zeros([self.numPixel, iPSD], float64)
        self.arErr = zeros([self.numPixel, iPSD], float64)
        # マスクデータを格納
        for i in range(self.numPixel):
            for j in range(iPSD):
                self.arData[i, j] = MASKVALUE
        self.matTOF = []
        self.matINT = []
        self.matERR = []
        self.searchId = Manyo.SearchInHeader(self.matrix)
        # PixelInfoのデータリストを準備
        self.PixelInfo = []
        for i in range(self.numPixel):
            psd_list = []
            for j in range(iPSD):
                psd_list.append((0.0, 0.0, 0.0))
            self.PixelInfo.append(psd_list)

        empty_flag = 0
        # make empty
        ec = None
        for i in range(self.matrix.PutSize()):
            hh = self.matrix(i).PutHeaderPointer()
            if hh.PutInt4("MASKED") == 0:
                for j in range(self.matrix(i).PutSize()):
                    hh_ec = self.matrix(i, j).PutHeaderPointer()
                    if hh_ec.PutInt4("MASKED") == 0:
                        ec = self.matrix(i, j)
                        break
                if ec is not None:
                    break

        xx = array(ec.PutXList())
        yy = ec.PutYList()
        for j in range(len(yy)):
            yy[j] = 0
        yy = array(yy)

        emp_psdTOF = []
        emp_psdINT = []
        for i in range(self.numPixel):
            emp_psdTOF.append(xx)
            emp_psdINT.append(yy)

        # ハッシュテーブルの数だけ繰り返す
        # print "psdID=",
        self.hTableMat = []  # [inamura 140128]
        for xi in range(len(self.hTable)):
            psdID = self.hTable[xi]

            if psdID >= 0:
                self.searchId.SearchArray("DETID", psdID)
                vect_index = self.searchId.PutResultIndex(0)
                retId = -1
                if vect_index.size() != 0:
                    retID = vect_index[0]
                if retID == -1:
                    self.masked.append(psdID)
                    # デテクタデータを書き込み
                    self.matTOF.append(emp_psdTOF)
                    self.matINT.append(emp_psdINT)
                    self.matERR.append(emp_psdINT)

                    if psdID >= len(self.hTableMat):
                        for kk in range(psdID - (len(self.hTableMat) - 1)):
                            self.hTableMat.append(-1)
                    self.hTableMat[psdID] = len(self.matTOF) - 1

                else:
                    # エレメントアレイのポインタを取得
                    ea = self.matrix.PutPointer(retID)
                    # エレメントアレイのヘッダからマスク情報を取得
                    eah = ea.PutHeader()
                    if eah.PutInt4("MASKED"):
                        self.masked.append(psdID)

                    # データマップの横軸インデックスを求める
                    psdTOF = []
                    psdINT = []
                    psdERR = []
                    # ピクセル数だけ読み出す
                    for i in range(self.numPixel):
                        if psdID not in self.masked:
                            ec = ea.PutPointer(i)
                            try:
                                ech = ec.PutHeader()
                            except:
                                mu.UtsusemiError(
                                    "Cannot load Header of ec(%d) in psdId(%d)" % (i, retID))
                                raise

                            yi = self.numPixel - i - 1
                            if self.isPixelReverse:
                                yi = i

                            tc = ec.Sum(ec.PutYKey(), ec.PutEKey())
                            self.arData[yi, xi] = tc.first
                            self.arErr[yi, xi] = tc.second

                            if ech.CheckKey(mu.UTSUSEMI_KEY_HEAD_PIXELPOLARANGLES) == 1:
                                angP = ech.PutDoubleVector(mu.UTSUSEMI_KEY_HEAD_PIXELPOLARANGLES)
                                angA = ech.PutDoubleVector(mu.UTSUSEMI_KEY_HEAD_PIXELAZIMANGLES)
                                posi = ech.PutDoubleVector(mu.UTSUSEMI_KEY_HEAD_PIXELPOSITION)
                                L2 = math.sqrt(
                                    posi[0] * posi[0] + posi[1] * posi[1] + posi[2] * posi[2])
                                self.PixelInfo[yi][xi] = (angP[0], angA[0], L2)
                        # ピクセルデータを取得
                        xx = array(ec.PutXList())
                        yy = array(ec.PutYList())
                        ee = array(ec.PutEList())
                        psdTOF.append(xx)
                        psdINT.append(yy)
                        psdERR.append(ee)
                    # デテクタデータを書き込み
                    if self.isPixelReverse:
                        psdTOF.reverse()
                        psdINT.reverse()
                        psdERR.reverse()
                    self.matTOF.append(psdTOF)
                    self.matINT.append(psdINT)
                    self.matERR.append(psdERR)

                    if psdID >= len(self.hTableMat):
                        for kk in range(psdID - (len(self.hTableMat) - 1)):
                            self.hTableMat.append(-1)
                    self.hTableMat[psdID] = len(self.matTOF) - 1

            self.progress += 1

        if mu.UtsusemiEnvGetDebugMode():
            mu.UtsusemiMessage("Masked:{}".format(self.masked))

    #########################################
    def GetMapData(self):
        """
        全検出単位のカウントマップを返す
        @param  無し        @retval カウントマップデータ(PSD×Pixecl の2次元配列)
        """
        return self.arData

    #########################################
    def RemakeMapData(self, tof1, tof2, hTable):
        """
        全検出単位のカウントマップを返す
        @param  tof1  　TOF 範囲始値
        @param  tof2　  TOF 範囲終値
        @param  hTable  PSD読み出し用のハッシュテーブル
        @retval カウントマップデータ(PSD×Pixecl の2次元配列)
        """

        code =\
            """
         double sum = 0.0;
         int i;

         for(int i=0; i < yy.ubound(0) ;i++) {
             if( tof(i) > tof2) {
                 break;
                 }
             if( tof(i) >= tof1) {
                 sum += yy(i);
             }
         }
         return_val = sum;

         """

        code_err =\
            """
         double sum = 0.0;
         int i;

         for(int i=0; i < ee.ubound(0) ;i++) {
             if( tof(i) > tof2) {
                 break;
                 }
             if( tof(i) >= tof1) {
                 sum += ee(i)*ee(i);
             }
         }
         return_val = sum;

         """

        # ハッシュテーブル内の数だけ繰り返す
        for xi in range(len(hTable)):
            psdID = hTable[xi]
            # 有効な検出器か
            if psdID >= 0:
                if self.IsValid(psdID):
                    # ピクセル数だけ読み出す
                    psd_index = self.hTableMat[psdID]
                    for j in range(self.numPixel):
                        try:
                            tof = self.matTOF[psd_index][j]
                            yy = self.matINT[psd_index][j]
                            ee = self.matERR[psd_index][j]
                        except:
                            mu.UtsusemiWarning("psdId=%d, j=%d, self.matTOF.size()=%d" % (
                                psdID, j, len(self.matTOF)))
                            mu.UtsusemiWarning("psd_index = %d" % (psd_index))
                            continue
                        # weave がインポートされているか
                        if wflag:
                            # C言語で積算
                            sum = weave.inline(
                                code, ['tof', 'yy', 'tof1', 'tof2'], type_converters=weave.converters.blitz)
                            sum_e2 = weave.inline(
                                code_err, ['tof', 'ee', 'tof1', 'tof2'], type_converters=weave.converters.blitz)
                        else:
                            # python で積算
                            sum = 0.0
                            sum_e2 = 0.0
                            for i in range(len(yy)):
                                # 範囲の終わりか
                                if tof[i] > tof2:
                                    break
                                # 範囲内か
                                if tof[i] >= tof1:
                                    sum += yy[i]
                                    sum_e2 += ee[i] * ee[i]
                        # 新たな値に入れ替える
                        self.arData[(self.numPixel - j - 1), xi] = sum
                        self.arErr[(self.numPixel - j - 1), xi] = math.sqrt(sum_e2)

                # Masked PSD
                else:
                    for j in range(self.numPixel):
                        self.arData[j, xi] = MASKVALUE

            self.npsd = psdID

        return self.arData

    #########################################
    def GetPSDArray(self, psd):
        """
        指定されたPSD のデータを返す
        @param  psd  psd No.
        @retval PSD のデータ
        """
        # Inamura
        self.searchId.SearchArray("DETID", psd)
        vect_index = self.searchId.PutResultIndex(0)
        if vect_index.size() == 0:
            return (None, None, None)

        psd = vect_index[0]
        # 中心のエレメントコンテナのTOFを取得
        ec0 = self.matrix(psd, int(self.numPixel / 2))
        tof = ec0.PutX()
        # リビニング後のY データの個数
        znum = len(tof) - 1

        # リビニング後のカウント数アレイ用に全点 0 のアレイを作成
        zz = zeros((znum, self.numPixel), float64)
        er = zeros((znum, self.numPixel), float64)

        # 1検出器分の強度データを取り出す
        for i in range(self.numPixel):
            # エレメントコンテナ取得
            ec = self.matrix(psd, i)
            # 中央のTOF 値でリビニング
            ecx = ec.ReBin(tof)
            yy = ecx.PutYList()
            ee = ecx.PutEList()
            # カウント数アレイとエラー値アレイへ格納
            for j in range(len(yy)):
                zz[j, i] = yy[j]
                er[j, i] = ee[j]

        return (tof, zz, er)

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

    def GetMax(self):
        """
        カウント数の最大値(マスク値を除く)
        @param  無し
        @retval 最大値
        """
        max = -1.0E10
        for i in range(len(self.arData)):
            for j in range(len(self.arData[0])):
                if self.arData[i, j] < MASKVALUE:
                    if self.arData[i, j] > max:
                        max = self.arData[i, j]
        if max == -1.0E10:
            mu.UtsusemiMessage("All is MASKVALUE")
            max = 1.0
        return max

    #########################################
    def GetMin(self):
        """
        カウント数の最小値
        @param  無し
        @retval 最小値
        """
        min = 1.0E10
        for i in range(len(self.arData)):
            for j in range(len(self.arData[0])):
                if self.arData[i, j] < min:
                    min = self.arData[i, j]
        if min == 1.0E10:
            mu.UtsusemiMessage("All is over 1.0E10")
            min = self.GetMax() - 1
        return min

    #########################################
    def GetTitle(self):
        """
        マトリックスのヘッダーよりランNo.とサンプル名を取得
        @param  無し
        @retval Run No. (文字列)
        """
        runNo = ""
        sample = ""
        try:
            # マトリックスのヘッダーを取得
            mth = self.matrix.PutHeader()
            # ヘッダーストリングを取得
            runNo = mth.PutString("RUNNUMBER")
        except:
            return ""

        return runNo

    #########################################
    def GetTOFRange(self):
        """
        データ読み出し時のTOF範囲を取得
        @param  無し
        @retval ヒストグラムデータ(X, Y, er, ヘッダー、xunit, yunit)
        """
        # 中心のエレメントコンテナのTOFを取得
        ec0 = None
        for i in range(self.matrix.PutSize()):
            hh = self.matrix(i).PutHeaderPointer()
            if hh.PutInt4("MASKED") == 0:
                for j in range(self.matrix(i).PutSize()):
                    hh_ec = self.matrix(i, j).PutHeaderPointer()
                    if hh_ec.PutInt4("MASKED") == 0:
                        ec0 = self.matrix(i, j)
                        break
                if ec0 is not None:
                    break

        tof = ec0.PutX()
        n = len(tof) - 1
        return (tof[0], tof[n])

    #########################################
    def GetHistogram(self, psdID, pixel):
        """
        PSD-ID からヒストグラムを取得
        @param  psdID PSD No
        @param  pixel Pixel No
        @retval ヒストグラムデータ(X, Y, er, ヘッダー、xunit, yunit)
        """

        self.searchId.SearchArray("DETID", psdID)
        vect_index = self.searchId.PutResultIndex(0)
        if vect_index.size() == 0:
            return None

        psdID = vect_index[0]

        # エレメントコンテナ取得
        ec = self.matrix(psdID, pixel)
        # Manyo データからプロッタ用データに変換
        pdat = self.ce.ConvertEC(ec)
        # トレースラベルを追加
        label = "%d-%d" % (psdID, pixel)
        pdat[3]["Label"] = label
        return pdat

    #########################################
    def IntegrateHistogram(self, lstPSDid):
        """
        指定されたヒストグラムを積算して返す
        ヘッダーの PSD-ID には積算情報を書きこむ
        @param  lstPSDid (ブロックNo.、PSD No.、Pixel No. のタプル　のリスト)
        @retval ヒストグラムデータ()
        """
        # 領域の中心のPSD と　Pixel を求める
        ps = []
        px = []
        for psdID in lstPSDid:
            if not psdID[0] in ps:
                ps.append(psdID[0])
            if not psdID[1] in px:
                px.append(psdID[1])
        pp = ps[int(len(ps) / 2.0)]
        xx = px[int(len(px) / 2.0)]

        self.searchId.SearchArray("DETID", pp)
        vect_index = self.searchId.PutResultIndex(0)
        if vect_index.size() == 0:
            return (None, None, None)

        pp = vect_index[0]

        # 領域の中心のエレメントコンテナのTOFを取得
        ec0 = self.matrix(pp, xx)
        tof = ec0.PutX()

        # 積算 PSD-ID の文字列を作る
        strSum = ""

        # リストに含まれるデータを積算
        n = 0.0

        # for PolarAngle(val,width), AzimAngle(val,width) and PixelPosi(x,y,z) average calculation
        # [deg],[deg],[mm],[mm],[mm]
        pInfo = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
        for psdID in lstPSDid:

            # Inamura
            self.searchId.SearchArray("DETID", psdID[0])
            vect_index = self.searchId.PutResultIndex(0)
            if vect_index.size() == 0:
                return (None, None, None)
            psd = vect_index[0]

            # 強度とエラーを取得
            ec = self.matrix(psd, psdID[1])

            # 領域中心のTOFでリビニング
            ecx = ec.Binning(tof)
            yy = ecx.PutYList()
            er = ecx.PutEList()
            yy = array(yy)
            er = array(er)

            # 最初か
            if strSum == "":
                sumyy = yy
                sumer = er * er
                hist = self.ce.ConvertEC(ec)
            # 2回目以降か
            else:
                # Y値リストとエラーを積算
                sumyy = sumyy + yy
                sumer = sumer + er * er

            # psdID の文字列を作成
            strDat = "%d-%d" % (psdID[0], psdID[1])
            # 積算文字列を作成
            if strSum == "":
                strSum = strSum + strDat
            else:
                strSum = strSum + "+" + strDat
            # 積算回数をカウント
            n += 1.0
            # Add PolarAngle, AzimAngle L2
            hh = ec.PutHeaderPointer()
            if hh.CheckKey(mu.UTSUSEMI_KEY_HEAD_PIXELPOLARANGLES) == 1:
                pv = hh.PutDoubleVector(mu.UTSUSEMI_KEY_HEAD_PIXELPOLARANGLES)
                pInfo[0] += pv[0]
                pInfo[1] += pv[1]
            if hh.CheckKey(mu.UTSUSEMI_KEY_HEAD_PIXELAZIMANGLES) == 1:
                pv = hh.PutDoubleVector(mu.UTSUSEMI_KEY_HEAD_PIXELAZIMANGLES)
                pInfo[2] += pv[0]
                pInfo[3] += pv[1]
            if hh.CheckKey(mu.UTSUSEMI_KEY_HEAD_PIXELPOSITION) == 1:
                pv = hh.PutDoubleVector(mu.UTSUSEMI_KEY_HEAD_PIXELPOSITION)
                pInfo[4] += pv[0]
                pInfo[5] += pv[1]
                pInfo[6] += pv[2]

        # 積算回数で割る
        sumyy = sumyy / n
        sumer = pylab.sqrt(sumer) / n    # 2009.06.15 Minakawa 修正
        for i in range(len(pInfo)):
            pInfo[i] = pInfo[i] / n

        # ヘッダー内の Label を、積算文字列に変更
        hist[3]["Label"] = strSum
        hist[1] = sumyy
        hist[2] = sumer
        # Add Average of PixelPosition, PolarAnger and AzimathAngle into the data header
        hist[3][mu.UTSUSEMI_KEY_HEAD_PIXELPOSITION] = (
            "{},{},{}".format(pInfo[4], pInfo[5], pInfo[6]))
        hist[3][mu.UTSUSEMI_KEY_HEAD_PIXELPOLARANGLES] = ("{},{}".format(pInfo[0], pInfo[1]))
        if pInfo[5] == 0.0:
            if pInfo[4] >= 0.0:
                pInfo[2] = 0.0
            else:
                pInfo[2] = 180.0
        else:
            LL = math.sqrt(pInfo[4] * pInfo[4] + pInfo[5] * pInfo[5])
            pInfo[2] = 180.0 / mm.MLF_PI * math.acos(pInfo[4] / LL) * pInfo[5] / math.sqrt(pInfo[5] * pInfo[5])
        hist[3][mu.UTSUSEMI_KEY_HEAD_PIXELAZIMANGLES] = ("{},{}".format(pInfo[2], pInfo[3]))

        # 積算後のヒストグラムデータに変更
        return hist

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

    def IsValid(self, psd):
        """
        マスクされている検出器かどうかを調べる
        @param   psd  PSD ID
        @retval True: 有効 or False: 無効
        """
        # マスクPSD のリスト中にあるか
        if psd in self.masked:
            return False
        else:
            return True

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

    def GetLineFromIntensities(self, psdRange, pixelRange, xAxis):
        """
        Get Intensity as plot along Pixel direction
        @param psdRange   tupple of minimum and maximum PSD
        @param pixelRange tupple of minimum and maximum PixelNo
        @param xAxis      "PSD" or "PIXEL" to be x-axis
        """
        if xAxis not in ["PSD", "PIXEL"]:
            raise

        min_psd = psdRange[0]
        max_psd = psdRange[1]
        min_pix = pixelRange[0]
        max_pix = pixelRange[1]

        xi_min = -1
        xi_max = -1
        for i in range(len(self.hTable)):
            if min_psd == self.hTable[i]:
                xi_min = i
                break
        for i in range(len(self.hTable)):
            if max_psd == self.hTable[i]:
                xi_max = i
                break

        if xi_min > xi_max:
            tmp = xi_min
            xi_min = xi_max
            xi_max = tmp

        yi_min = self.numPixel - max_pix - 1
        yi_max = self.numPixel - min_pix - 1

        psd_in = []
        for i in range(xi_min, (xi_max + 1)):
            if self.hTable[i] != -1:
                psd_in.append(i)

        pixel_in = range(yi_min, (yi_max + 1))

        xx = []
        yy = []
        ee = []
        n_yy = []

        if xAxis == "PSD":
            for i in range(len(psd_in)):
                xx.append(self.hTable[psd_in[i]])
                yy.append(0.0)
                ee.append(0.0)
                n_yy.append(0.0)
            for pix in pixel_in:
                for i in range(len(psd_in)):
                    if self.arData[pix, psd_in[i]] < MASKVALUE and self.arErr[pix, psd_in[i]] >= 0.0:
                        yy[i] += self.arData[pix, psd_in[i]]
                        ee[i] += self.arErr[pix, psd_in[i]] * \
                            self.arErr[pix, psd_in[i]]
                        n_yy[i] += 1.0
            for i in range(len(xx)):
                # yy[i] = yy[i]/( float(len(pixel_in)) )
                # ee[i] = math.sqrt(ee[i])/( float(len(pixel_in)) )
                if n_yy[i] != 0.0:
                    yy[i] = yy[i] / (n_yy[i])
                    ee[i] = math.sqrt(ee[i]) / (n_yy[i])

        else:
            for i in range(len(pixel_in)):
                xx.append(self.numPixel - (pixel_in[i] + 1))
                yy.append(0.0)
                ee.append(0.0)
                n_yy.append(0.0)
            for psd in psd_in:
                for i in range(len(pixel_in)):
                    if self.arData[pixel_in[i], psd] < MASKVALUE and self.arErr[pixel_in[i], psd] >= 0.0:
                        yy[i] += self.arData[pixel_in[i], psd]
                        ee[i] += self.arErr[pixel_in[i], psd] * \
                            self.arErr[pixel_in[i], psd]
                        n_yy[i] += 1.0
            for i in range(len(xx)):
                # yy[i] = yy[i]/( float(len(psd_in)) )
                # ee[i] = math.sqrt(ee[i])/( float(len(psd_in)) )
                if n_yy[i] != 0.0:
                    yy[i] = yy[i] / (n_yy[i])
                    ee[i] = math.sqrt(ee[i]) / (n_yy[i])

        label = "%d-%d,%d-%d(along %s)" % (min_psd,
                                           min_pix, max_psd, max_pix, xAxis)
        sub_title = " Along {}:".format(xAxis)
        if xAxis == "PSD":
            sub_title += "{}-{} / pixel range:{}-{}".format(
                min_psd, max_psd, min_pix, max_pix)
        else:
            sub_title += "{}-{} / PSD range:{}-{}".format(
                min_pix, max_pix, min_psd, max_psd)
        mu.UtsusemiMessage("label={}".format(label))
        header = {"Label": label, "SubTitle": sub_title}

        ret = []
        if xAxis == "PSD":
            ret = [xx, yy, ee, header, "DETID", "Counts"]
        else:
            ret = [xx, yy, ee, header, "PIXEL", "Counts"]

        return ret


#######################################
#  DQuasiObj
#######################################
class DQuasiObj(object):

    """
    擬似データのハンドリングクラス
    """
    # X値リスト(デバッグ用ダミーデータ)
    xx = [0, 3, 5, 6, 9, 11, 12, 13,
          14, 16, 18, 21, 22, 23, 24, 38, 40]
    # Y値リスト(デバッグ用ダミーデータ)
    yy = [0.0, 0.0, 1.0, 5.0, 0.0, 0.0, 100.0, 250.0,
          180.0, 90.0, 0.0, 0.0, 2.0, 14.0, 0.0, 0.0]
    # エラーリスト(デバッグ用ダミーデータ)
    er = er = [1, 1, 1, 2, 1, 1, 6, 7, 5, 4, 2, 0, 2, 3, 2, 1]

    #########################################
    def __init__(self, parent, fname, hTable):
        """
        擬似データファイル読み込み、カウントマップを作成
        @param  parent    親クラスのインスタンス
        @param  fname  　擬似データファイル名
        @param  hTable　 PSD読み出し用のハッシュテーブル
        @retval 無し
        """
        self.numBank = parent.numBank
        self.numPSD = parent.numPSD
        self.numPixel = parent.numPixel

        # 疑似データファイルを開く
        datFile = open(fname, 'r')

        dataList = []
        dummy = []
        # バンクギャップ用のダミーデータを準備
        for i in range(self.numPixel):
            dummy.append(MASKVALUE)

        i = 1
        psd = 0
        for line in datFile:
            try:
                # 1検出器分のデータを読み込んで、実数に変換
                data = map(float, line[:-1].split(','))
            except:
                i += 1
                if i > self.numBank:
                    break
                # バンクギャップ用のダミーデータを作成
                dataList.append(dummy)
                dataList.append(dummy)
                psd += 2
            else:
                if hTable[psd] >= 0:
                    # データを追加
                    data.reverse()
                    dataList.append(data)
                else:
                    dataList.append(dummy)
                psd += 1

        # データアレイを準備
        self.arData = zeros((self.numPixel, (self.numPSD + 2) * self.numBank - 2))

        # 縦横を入れ替え
        for i in range(self.numPixel):
            for j in range((self.numPSD + 2) * self.numBank - 2):
                self.arData[i][j] = dataList[j][i]
        # 疑似データファイルをクローズ
        datFile.close()

    #########################################
    def GetMapData(self):
        """
        全検出単位のカウントマップを返す
        @param  無し
        @retval カウントマップデータ(PSD×Pixecl の2次元配列)
        """
        return self.arData + 0.05

    #########################################
    def RemakeMapData(self, tof1, tof2, hTable):
        """
        全検出単位のカウントマップを返す
        @param  tof1  　TOF 範囲始値
        @param  tof2　  TOF 範囲終値
        @param  hTable  PSD読み出し用のハッシュテーブル
        @retval カウントマップデータ(PSD×Pixecl の2次元配列)
        """
        return self.arData + 0.05

    #########################################
    def GetPSDArray(self, psd):
        """
        指定されたPSD のデータを返す
        @param  psd  psd No.
        @retval PSD のデータ
        """

        # Y データの個数
        tof = arange(5000.0, 15100.0, 100.0)
        znum = len(tof) - 1

        # 全点 0 のアレイを作成
        zz = zeros((znum, self.numPixel), float64)
        er = zeros((znum, self.numPixel), float64)

        # 1検出器分の強度データを取り出す
        for i in range(self.numPixel):
            k = 100
            for j in range(znum):
                while True:
                    if k == len(self.arData[0]):
                        break
                    yy = self.arData[i, k]
                    if self.arData[i, k] < MASKVALUE:
                        break
                    k += 1

                zz[j, i] = yy
                if k == len(self.arData[0]):
                    break
                k += 1
        zz[10, 10] = -1.5
        return (tof, zz, er)

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

    def GetMax(self):
        """
        カウント数の最大値(マスク値を除く)
        @param  無し
        @retval 最大値
        """
        max = 1.0
        for i in range(len(self.arData)):
            for j in range(len(self.arData[0])):
                if self.arData[i, j] < MASKVALUE:
                    if self.arData[i, j] > max:
                        max = self.arData[i, j]
        return max

    #########################################
    def GetTitle(self):
        """
        マトリックスのヘッダーよりランNo.とサンプル名を取得
        @param  無し
        @retval Run No. (文字列)
        """
        return "AMT0002"

    #########################################
    def GetTOFRange(self):
        """
        データ読み出し時のTOF範囲を取得
        @param  無し
        @retval ヒストグラムデータ(X, Y, er, ヘッダー、xunit, yunit)
        """
        n = len(self.xx) - 1
        return (self.xx[0], self.xx[n])

    #########################################
    def GetHistogram(self, psdID, pixel):
        """
        疑似ヒストグラムデータを返す
        @param  psdID PSD No
        @param  pixel Pixel No
        @retval x, y, er のアレイ、x軸単位、Y軸単位、ヘッダーディクショナリのタプル
        """
        # ヘッダーディクショナリ作成
        strPSD = "%d-%d" % (psdID, pixel)
        header = {"Label": strPSD, "RunNo": "AMT00001", }

        return (self.xx, self.yy, self.er, header, "micro-sec", "Newtrons")

    #########################################
    def IntegrateHistogram(self, lstPSDid):
        """
        指定されたヒストグラムを積算して返す
        ヘッダーの PSD-ID には積算情報を書きこむ
        @param  lstPSDid (ブロックNo.、PSD No.、Pixel No. のタプル　のリスト)
        @retval ヒストグラムデータ()
        """

        # 積算 PSD-ID の文字列を作る
        strSum = ""

        # リストに含まれるデータを積算
        for psdID in lstPSDid:
            # psdID の文字列を作成
            strDat = "%d-%d" % (psdID[0], psdID[1])
            # 積算文字列を作成
            if strSum == "":
                strSum = strSum + strDat
            else:
                strSum = strSum + "+" + strDat

        header = {"Label": strSum, "RunNo": "AMT00001", }
        return (self.xx, self.yy, self.er, header, "micro-sec", "Newtrons")

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

    def IsValid(self, psd):
        """
        マスクされている検出器かどうかを調べる
        @param   psd  PSD ID
        @retval True: 有効　or  False: 無効
        """
        return True

#######################################
#  MapFrame
#######################################


class MapFrame(QtWidgets.QMainWindow):
    """
    検出器マップ表示画面クラス
    """

    #########################################
    def __init__(self, parent, matrix, isPixelReverse=False):
        """
        検出器マップ表示処理のコンストラクタ
        @param  parent 親画面のインスタンス
        @param  matrix エレメントアレイマトリックス
        @retval 無し
        """
        self.matrix = matrix  # [inamura 140128]
        # 初期化
        self.logFlag = False
        self.dragFlag = False
        self.d2 = None
        self.isPixelReverse = isPixelReverse
        # プロッタ番号の初期化
        self.order = 0
        # プロッタリストの準備
        self.plots = []
        # カレントプロッタ
        self.plot = None

        # リソースからメイン画面を取得
        super(MapFrame, self).__init__(parent)
        self.frame = Ui_mapframe()
        # リソースからフレームが取得できなかった
        if self.frame is None:
            return
        self.frame.setupUi(self)
        # アイコンを設定
        # self.frame.SetIcon(Images().GetMainIcon())
        # メニューバーを取得
        self.MenuWidgets = {}
        menu_FileMenu = self.findChild(QtWidgets.QMenu, u'menuFile')
        self.MenuWidgets['open'] = self.findChild(QAction, u'actionOpen')
        self.MenuWidgets['save'] = self.findChild(QAction, u'actionSave')
        self.MenuWidgets['save_as'] = self.findChild(QAction, u'actionSave_as')
        self.MenuWidgets['open'].setEnabled(False)
        self.MenuWidgets['save'].setEnabled(False)
        self.MenuWidgets['save_as'].setEnabled(False)
        self.MenuWidgets['print'] = self.findChild(QAction, u'actionPrint')
        self.MenuWidgets['exit'] = self.findChild(QAction, u'actionExit')
        menu_DisplayMenu = self.findChild(QtWidgets.QMenu, u'menuDisplay')
        self.MenuWidgets['newplot'] = self.findChild(
            QAction, u'actionNew_Plotter')
        self.MenuWidgets['logscale'] = self.findChild(
            QAction, u'actionLog_Scale')

        # グラフ描画用のパネルを取得
        map_panel_layout = QtWidgets.QVBoxLayout(
            self.findChild(QtWidgets.QWidget, u'mapPanel'))
        # map_panel_layout.setObjectName("mappanel")

        # グラフ描画用のフィギュアとキャンバスを作成
        self.fig = Figure(None, 80)
        self.canvas = FigCanvas(self.fig)

        # ツールバー作成
        self.toolbar = uGaoUtils.ChartToolBar(self, 0)
        self.toolbar.canvas.draw_idle()

        # キャンバスとツールバーを配置
        map_panel_layout.addWidget(self.toolbar)
        map_panel_layout.addWidget(self.canvas)

        # コンフィギュレーションを取得
        try:
            # 分光器名を取得
            instrument = mu.UtsusemiEnvGetInstCode()
            # 検出器情報を取得
            self.numBank = 1
            self.numPSD = matrix.PutSize()
            # PSD 情報(ハッシュテーブル)を取得
            self.numPixel = 0
            for i in range(matrix.PutSize()):
                hh = matrix(i).PutHeaderPointer()
                if hh.PutInt4("MASKED") == 0:
                    self.numPixel = matrix(i).PutSize()

            self.hTable = []

            pHeader = matrix.PutHeader()
            instrument = pHeader.PutString("INSTRUMENT")

            if (pHeader.CheckKey("BANKIDLIST") == 1):
                bankIdList = pHeader.PutInt4Vector("BANKIDLIST")
                self.numBank = len(bankIdList)

                bankSizeList = pHeader.PutInt4Vector("BANKSIZELIST")
                SIH = Manyo.SearchInHeader(matrix)

                for i in range(len(bankIdList)):
                    bankId = bankIdList[i]
                    self.numPSD = bankSizeList[i]
                    aBank_hTable = []
                    for j in range(self.numPSD + 2):
                        aBank_hTable.append(-1)

                    if (SIH.SearchArray("BANKID", bankId)):
                        results = SIH.PutResultIndex(0)
                        for j in range(len(results)):
                            header = matrix(results[j]).PutHeader()
                            index_in_bank = header.PutInt4("INDEXINBANK")
                            aBank_hTable[index_in_bank] = header.PutInt4(
                                "DETID")
                    else:
                        mu.UtsusemiWarning("Failed SearchArray")

                    self.hTable.extend(aBank_hTable)

                self.hTable = self.hTable[:-2]
                del SIH
            else:
                for i in range(self.numPSD):
                    if matrix(i).PutHeaderPointer().PutInt4("MASKED") == 1:
                        self.hTable.append(-1)
                    else:
                        self.hTable.append(
                            matrix(i).PutHeaderPointer().PutInt4("DETID"))

            if mu.UtsusemiEnvGetDebugMode():
                mu.UtsusemiMessage("hTable={}".format(self.hTable))

        except uGaoUtils.PlotException as ex:
            uGaoUtils.PlotMessage(self.frame, ex)
            self.OnClose()
            return

        # フレームのタイトルに分光器名を付加
        strT1 = self.windowTitle()
        strT2 = " for %s" % instrument
        self.setWindowTitle(strT1 + strT2)

        # イベントハンドラーの設定
        self._SetEventHandler()

        # メイン画面を表示
        self.show()

        time.sleep(0.1)  # [inamura 100726] for stability on command line

        # 引数が文字列であったなら
        if isinstance(matrix, str):
            # 疑似データファイル読み込み
            self.do = DQuasiObj(self, matrix, self.hTable)
            self._DispChart()
        else:
            # エレメントコンテナマトリックスをロード
            self.do = DMatrixObj(
                self, matrix, self.hTable, self.isPixelReverse)
            self.do.ReadMatrix(self._DispChart)

        # Set working dir
        work_path = mu.UtsusemiEnvGetWorkDir()
        if work_path != "":
            os.chdir(work_path)

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

    def _DispChart(self):
        """
        検出器マップチャートを描画する
        @param   無し
        @retval 無し
        """
        # マップデータの取得
        self.Z = self.do.GetMapData()
        self.datMax = self.do.GetMax()
        self.zMax = self.datMax

        self.datMin = self.do.GetMin()
        if (self.logFlag) and (self.datMin <= 0.0):
            self.zMin = 1.0E-20
        else:
            self.zMin = self.datMin

        self.yminTx.setText(("%g" % self.zMin))
        self.ymaxTx.setText(("%g" % self.zMax))

        self.PixelInfo = self.do.PixelInfo

        # マップデータからマスクデータ作成
        self.Zm = ma.masked_where(self.Z >= MASKVALUE, self.Z)

        # TOF Range の初期値を設定
        tof1, tof2 = self.do.GetTOFRange()
        # 始値のテキストボックスに値を設定
        strtof1 = "%d" % tof1
        strtof2 = "%d" % tof2
        self.tofTx1.setText(strtof1)
        self.tofTx2.setText(strtof2)

        # PSD の最大値を検索
        self.maxPSD = 0
        for pnum in self.hTable:
            if pnum > self.maxPSD:
                self.maxPSD = pnum

        # 検出器マップの描画
        self.PlotChart()

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

    def _SetEventHandler(self):
        """
        イベントハンドラーの登録
        @param  None
        @retval 無し
        """
        # プリントボタンのリスナー登録
        uGaoUtils.IFEvtProp(0).AddListner('print', self.OnPrint)
        # 保存ボタンのリスナー登録
        uGaoUtils.IFEvtProp(0).AddListner('save', self.OnSave)

        # ボタンのイベントハンドラ登録
        self.findChild(QtWidgets.QPushButton,
                       u'btRedraw').clicked.connect(self.OnBtRedraw)
        self.findChild(QtWidgets.QPushButton,
                       u'bt2dPlot').clicked.connect(self.OnBt2dPlot)
        self.findChild(QtWidgets.QPushButton,
                       u'btAppend').clicked.connect(self.OnBtAppend)
        self.findChild(QtWidgets.QPushButton,
                       u'btSumm').clicked.connect(self.OnBtSumm)
        self.findChild(QtWidgets.QPushButton,
                       u'btNewPlot').clicked.connect(self.OnNewPlot)
        self.findChild(QtWidgets.QPushButton,
                       u'btRedraw').clicked.connect(self.OnBtRedraw)
        self.findChild(QtWidgets.QPushButton, u'btIntSet').clicked.connect(
            self.OnBtRedrawInt)
        self.findChild(QtWidgets.QPushButton,
                       u'btIntClear').clicked.connect(self.OnBtRedraw)

        # メニューのイベントハンドラ登録
        self.MenuWidgets['print'].triggered.connect(self.OnPrint)
        self.MenuWidgets['exit'].triggered.connect(self.OnClose)
        self.MenuWidgets['newplot'].triggered.connect(self.OnNewPlot)
        self.MenuWidgets['logscale'].triggered.connect(self.OnLog)

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

        # マウスイベントのリスナ登録
        self.canvas.mpl_connect('motion_notify_event', self.OnMouseMove)
        self.canvas.mpl_connect('button_press_event', self.OnBtDown)
        self.canvas.mpl_connect('button_release_event', self.OnBtUp)

        # テキストボックスのコントロール取得
        self.psdTx0 = self.findChild(QtWidgets.QLineEdit, u'psd0')
        self.psdTx1 = self.findChild(QtWidgets.QLineEdit, u'PSD1')
        self.pxTx1 = self.findChild(QtWidgets.QLineEdit, u'pixel1')
        self.psdTx2 = self.findChild(QtWidgets.QLineEdit, u'PSD2')
        self.pxTx2 = self.findChild(QtWidgets.QLineEdit, u'pixel2')
        self.tofTx1 = self.findChild(QtWidgets.QLineEdit, u'tof1')
        self.tofTx2 = self.findChild(QtWidgets.QLineEdit, u'tof2')

        # リストのコントロール取得
        self.plist = self.findChild(QtWidgets.QListWidget, u'plotList')
        self.plist.itemSelectionChanged.connect(self.OnListSelect)

        # 検出器情報のコントロール取得
        self.iftxt = self.findChild(QtWidgets.QLabel, u'infotxt')

        # スライダーのイベントハンドラ登録

        # リソースからスライダーのID を取得
        self.slider = self.findChild(QtWidgets.QSlider, u'slider')
        # スライダーのイベントハンドラーを設定
        self.slider.valueChanged[int].connect(self.OnSlider)
        self.slider.setMinimum(0)
        self.slider.setMaximum(99)

        self.yminTx = self.findChild(QtWidgets.QLineEdit, u'ymin')
        self.ymaxTx = self.findChild(QtWidgets.QLineEdit, u'ymax')
        self.cbXax = self.findChild(QtWidgets.QComboBox, u'cbXaxisOfPlot')

    #########################################
    def PlotChart(self):
        """
        検出器マップチャートを描画する
        @param   無し
        @retval 無し
        """
        # フィギュアをクリア
        self.fig.clf()
        # タイトルを取得
        runNo = self.do.GetTitle()
        strRunNo = "Run No.: " + runNo

        # グラフのタイトルとして、ランNo.を表示
        self.fig.text(0.08, 0.94, strRunNo)
        x0 = float(self.tofTx1.text())
        x1 = float(self.tofTx2.text())
        y0 = float(self.yminTx.text())
        y1 = float(self.ymaxTx.text())
        strInfo1 = "X-axis Range\nIntensity Range"
        strInfo2 = ": %g - %g\n: %g - %g" % (x0, x1, y0, y1)
        self.fig.text(0.20, 0.905, strInfo1)
        self.fig.text(0.32, 0.905, strInfo2)

        x = arange(0, (len(self.hTable) + 1), 1)
        y = arange(0, (self.numPixel + 1), 1)
        X, Y = meshgrid(x, y)

        # 検出器マップ用のサブプロットを準備
        self.ax = self.fig.add_subplot(111)
        self.ax.set_position([0.08, 0.15, 0.89, 0.73])

        # カラーバー用の座標軸を準備
        axc = self.fig.add_axes([0.68, 0.93, 0.25, 0.03])

        # カーソル表示用のサブプロットを準備(透過モード)
        self.cursol = self.fig.add_subplot(111, frameon=False)
        self.cursol.set_position([0.08, 0.15, 0.89, 0.73])

        if matplotlib_verNo >= 3030:
            self.cursol.sharex(self.ax)
            self.cursol.sharey(self.ax)

        # マスクデータの色作成(白色)
        # cmap = cm.jet
        # matplotlib.cm.get_cmap was deprecated in matplotlib 3.7
        # if matplotlib.__version__ > 3.7.0
        if matplotlib_verNo > 3070:
            cmap = copy.copy(matplotlib.colormaps['jet'])
        else:
            cmap = copy.copy(pylab.mpl.cm.get_cmap('jet'))
        cmap.set_bad('w', 1.0)

        # ログ表示か
        if self.logFlag:
            # カラーマップをログモードで表示
            pc = self.ax.pcolormesh(X, Y, self.Zm, cmap=cmap,
                                    norm=LogNorm(vmin=self.zMin, vmax=self.zMax))
        else:
            # カラーマップをリニアモードで表示
            pc = self.ax.pcolormesh(X, Y, self.Zm, cmap=cmap,
                                    norm=pylab.Normalize(vmin=self.zMin, vmax=self.zMax))

        # カラーマップと関連付けて、カラーバーを表示
        self.fig.colorbar(pc, cax=axc, orientation='horizontal')
        # カラーバーの座標ラベルの文字を小さくする
        xlabels = pylab.getp(axc, 'xticklabels')
        pylab.setp(xlabels, size=7)

        # チャートを整える
        self.ArrangeChart()

        # グラフを再表示
        self.canvas.draw()

    #########################################
    def ArrangeChart(self):
        """
        検出器マップチャートを整える
        @param   無し
        @retval 無し
        """
        # X軸範囲とY軸範囲をセット
        self.ax.set_xlim((0.0, len(self.hTable)))
        self.ax.set_ylim((0.0, self.numPixel))
        self.cursol.set_xlim((0.0, len(self.hTable)))
        self.cursol.set_ylim((0.0, self.numPixel))

        # 検出器マップの座標を消す
        self.ax.set_xticks([])
        self.ax.set_yticks([])

        # カーソル用サブプロットの座標を消す
        self.cursol.set_xticks([])
        self.cursol.set_yticks([])

        # ピクセル番号をグラフ左に表示
        hf = self.numPixel / 2 - 1
        strHf = "%d" % hf
        strPx = "%d" % (self.numPixel - 1)
        strOrg = "0"
        if self.isPixelReverse:  # [inamura 160508]
            strPx = "0"
            strOrg = "%d" % (self.numPixel - 1)
        # バンク番号をグラフ下に表示
        self.ax.text(-3, (self.numPixel - 1), strOrg, horizontalalignment='right')
        self.ax.text(-3, hf, strHf, horizontalalignment='right')
        self.ax.text(-3, 0, strPx, horizontalalignment='right')

        self.fig.text(0.5, 0.07, "Bank", horizontalalignment='center')
        """[inamura 140128]
        xx = 0.09
        xP = self.numPSD
        for i in range(self.numBank):
            # バンク No. 表示
            num = i+1.0
            strBk = "%d" % num
            bx = i*(self.numPSD+2) + self.numPSD / 2 + 1.0
            self.ax.text(bx, -7, strBk, horizontalalignment='center')
            xx += 0.084
            # バンク境界表示
            if not num == self.numBank:
                #self.ax.axvspan(xP, xP+2, facecolor='0.4')
                self.ax.axvspan(xP-0.2, xP+2, facecolor='0.4') ## [inamura 100726] for matplotlib 0.98
                xP = xP + 2 + self.numPSD
        """

        bankIdList = self.matrix.PutHeaderPointer().PutInt4Vector("BANKIDLIST")
        bankSizeList = self.matrix.PutHeaderPointer().PutInt4Vector("BANKSIZELIST")
        psd_offset = 0
        if len(bankSizeList) != 0:
            xP = bankSizeList[0]
            for i in range(bankIdList.size()):
                strBk = "%d" % bankIdList[i]
                # bx = psd_offset + bankSizeList[i]/2.0 +1.0
                bx = psd_offset + bankSizeList[i] / 2.0 - 0.5  # +1.0
                self.ax.text(bx, -7, strBk, horizontalalignment='center')
                # psd_offset += bankSizeList[i]
                psd_offset += (bankSizeList[i] + 2.0)

                if (i != (bankIdList.size() - 1)):
                    self.ax.axvspan((xP - 0.2), (xP + 2), facecolor='0.4')
                xP = xP + 2 + bankSizeList[i]

    #########################################
    def UpdateStatusBar(self, event=None):
        """
        マウス位置の検出器情報を表示
        @param   event イベント情報
        @retval 無し
        """
        # マウス位置を取得
        x, y = int(event.xdata), int(event.ydata)
        # Mouse position check (ignore color bar area)
        if x >= len(self.hTable) or y >= len(self.Z):
            return
        # マウス位置の PSD-ID を取得
        pixel = self.numPixel - (y + 1)
        psd = self.hTable[x]
        # マウス位置の カウント数を取得
        counts = self.Z[y, x]
        # 検出器があるか
        if self.hTable[x] >= 0:
            strSTDID = "DetID-Pixel: %03d -%03d \n" % (psd, pixel)
            # 有効な検出器か
            if self.do.IsValid(psd):
                strCounts = "     2theta:%+08.3f  Azim:%+08.3f  L2:%08.2f\n" % self.PixelInfo[y][x]
                strCounts += "     Counts: %d" % counts
            else:
                strCounts = "     2theta:xxxx.xxx  Azim:xxxx.xxx  L2:xxxxx.xx\n"
                strCounts += "     Counts: xx\n"
        # 無効な検出器の場合は情報を表示しない
        else:
            strSTDID = "DetID-Pixel: xxx -xxx \n"
            strCounts = "     2theta:xxxx.xxx  Azim:xxxx.xxx  L2:xxxxx.xx\n"
            strCounts += "     Counts: xx"

        # マップの下にマウス位置の情報を表示
        self.iftxt.setText(strSTDID + strCounts)

    #########################################
    def OnMouseMove(self, event=None):
        """
        マウス-ボタンクリックイベント処理
        @param   event イベント情報
        @retval 無し
        """
        # マウス位置がグラフ内かどうかをチェック
        if not event.inaxes:
            return
        # グラフ内であっても、None が来ることがあるのでプロテクト
        if event.ydata is None or event.xdata is None:
            return

        # ドラッグ中であれば
        if self.dragFlag:
            # カーソル用サブプロットをクリア
            self.cursol.clear()
            if matplotlib_verNo >= 3030:
                self.cursol.sharex(self.ax)
                self.cursol.sharey(self.ax)
            else:
                self.cursol.set_xlim(self.ax.get_xlim())
                self.cursol.set_ylim(self.ax.get_ylim())

            # if version of matplotlib > 0.98
            if matplotlib_verNo > 980:
                self.cursol.autoscale(False)

            # 現在のY　レンジを取得
            y0, y1 = self.cursol.set_ylim()

            if (event.ydata - y0) > self.ydata:
                yy0 = self.ydata
                yy1 = event.ydata
            else:
                yy0 = event.ydata
                yy1 = self.ydata

            if (event.xdata - y0) > self.xdata:
                xx0 = self.xdata
                xx1 = event.xdata
            else:
                xx0 = event.xdata
                xx1 = self.xdata
            # Y の比率を計算
            ymin = (yy0 - y0) / (y1 - y0)
            ymax = (yy1 - y0) / (y1 - y0)

            self.cursol.axvspan(xx0, xx1, ymin=ymin,
                                ymax=ymax, facecolor='w', alpha=0.4)
            # カーソル用サブプロットの座標を消す
            self.cursol.set_xticks([])
            self.cursol.set_yticks([])
            # グラフを再表示
            self.canvas.draw()

        # ドラッグ中でなければ
        else:
            # マウス位置の検出器ヘッダー情報を表示
            self.UpdateStatusBar(event)

    #########################################
    def OnBtDown(self, event=None):
        """
        マウス-ボタンクリックイベント処理
        @param   event イベント情報
        @retval 無し
        """
        # パン処理中またはズーム処理中であれば、以下の処理を実行しない
        if matplotlib_ver < NAVIGATIONTOOLBAR_CHANGED_VER:
            if self.toolbar._active == 'PAN' or self.toolbar._active == 'ZOOM':
                return
        else:
            if self.toolbar._actions['pan'].isChecked() or self.toolbar._actions['zoom'].isChecked():
                return
        if event.inaxes:
            # ドラッグ開始フラグをセット
            self.dragFlag = True
            # ドラッグ時のカーソル表示用に、開始位置を保存
            self.xdata = event.xdata
            self.ydata = event.ydata
            # クリック位置のPSD-ID を保存
            self.x0, self.y0 = int(event.xdata), int(event.ydata)
        else:
            self.x0 = -1

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

    def OnBtUp(self, event=None):
        """
        マウス-ボタンアップイベント処理
        @param   event イベント情報
        @retval 無し
        """

        # パン処理中またはズーム処理中であれば、以下の処理を実行しない
        if matplotlib_ver < NAVIGATIONTOOLBAR_CHANGED_VER:
            if self.toolbar._active == 'PAN' or self.toolbar._active == 'ZOOM':
                return
        else:
            if self.toolbar._actions['pan'].isChecked() or self.toolbar._actions['zoom'].isChecked():
                return
        # ドラッグ開始時にグラフ外だったか
        if not event.inaxes or self.x0 < 0:
            return

        # ドラッグ中であれば
        if self.dragFlag:
            # ドラッグフラグを解除
            self.dragFlag = False
            # ドラッグカーソルを消す
            # self.cursol.clear()
            # カーソル用サブプロットの座標を消す
            self.cursol.set_xticks([])
            self.cursol.set_yticks([])
            # グラフを再表示
            self.canvas.draw()

        # ボタンアップ時の位置を取得
        x2, y2 = int(event.xdata), int(event.ydata)
        # 始値より小さければ入れ替える
        if self.x0 > x2:
            x1 = x2
            x2 = self.x0
        else:
            x1 = self.x0

        if self.y0 < y2:
            y1 = y2
            y2 = self.y0
        else:
            y1 = self.y0

        pixel1 = self.numPixel - y1 - 1
        pixel2 = self.numPixel - y2 - 1

        xx = x1
        psi1 = -1
        psi2 = -1

        while xx <= x2:
            # 最も近い PSD
            if self.hTable[xx] >= 0 and self.do.IsValid(self.hTable[xx]):
                if psi1 < 0:
                    psi1 = self.hTable[xx]
                    psi2 = self.hTable[xx]
                else:
                    psi2 = self.hTable[xx]
            xx += 1
        # データ無しか
        if psi1 < 0:
            self.psdTx0.setText("")
            self.psdTx1.setText("")
            self.pxTx1.setText("")
            self.psdTx2.setText("")
            self.pxTx2.setText("")
            return

        # 始値のテキストボックスに値を設定
        strpsd = "%d" % psi1
        strpx = "%d" % pixel1
        self.psdTx0.setText(strpsd)
        self.psdTx1.setText(strpsd)
        self.pxTx1.setText(strpx)
        # ボタン押下時と位置が同じであるなら、ドラッグではなくクリック
        if x1 == x2 and y1 == y2:
            # 終値のテキストボックスをクリア
            self.psdTx2.setText("")
            self.pxTx2.setText("")

            # Cursor面をクリアしたのちは必ず再設定が必要
            self.cursol.clear()
            if matplotlib_verNo >= 3030:
                self.cursol.sharex(self.ax)
                self.cursol.sharey(self.ax)
            else:
                self.cursol.set_xlim(self.ax.get_xlim())
                self.cursol.set_ylim(self.ax.get_ylim())
            if matplotlib_verNo > 980:
                self.cursol.autoscale(False)
            self.cursol.set_xticks([])
            self.cursol.set_yticks([])
            self.canvas.draw()

        else:
            # 終値のテキストボックスに値を設定
            strpsd = "%d" % psi2
            strpx = "%d" % pixel2
            self.psdTx2.setText(strpsd)
            self.pxTx2.setText(strpx)

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

    def OnBtAppend(self, event=None):
        """
        Appendボタン押下イベント処理
        @param   event イベント情報
        @retval 無し
        """
        # 個別にデータをプロッタへ追加
        self.AddToPlot(False)

    #########################################
    def OnBtRedraw(self, event=None):
        """
        指定されたTOFレンジからカウント数を
        再計算し、マップを再表示
        @param   event イベント情報
        @retval 無し
        """
        try:
            try:
                # TOF Range をテキストボックスから取得
                tof1 = float(self.tofTx1.text())
                tof2 = float(self.tofTx2.text())
            except:
                # エラーメッセージを表示
                raise uGaoUtils.PlotException('Common', 'C027', ("TOF Range",))
            if tof1 > tof2:
                raise uGaoUtils.PlotException(
                    'Common', 'C010', ("TOF Range End", "TOF Range First"))
        except uGaoUtils.PlotException as ex:
            uGaoUtils.PlotMessage(self.frame, ex)
            return
        # カーソルを砂時計に変更
        QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
        # マップデータを再作成
        self.Z = self.do.RemakeMapData(tof1, tof2, self.hTable)
        # カーソルを戻す
        QtWidgets.QApplication.restoreOverrideCursor()

        self.datMax = self.do.GetMax()
        self.zMax = self.datMax

        self.datMin = self.do.GetMin()
        if (self.logFlag) and (self.datMin <= 0.0):
            self.zMin = 1.0E-20
        else:
            self.zMin = self.datMin

        self.yminTx.setText(("%g" % self.zMin))
        self.ymaxTx.setText(("%g" % self.zMax))

        # マップデータからマスクデータ作成
        self.Zm = ma.masked_where(self.Z >= MASKVALUE, self.Z)

        # マップを再描画
        self.PlotChart()

    #########################################
    def OnBtRedrawInt(self, event=None):
        """
        指定されたIntensityレンジとTOFレンジからカウント数を
        再計算し、マップを再表示
        @param   event イベント情報
        @retval 無し
        """
        try:
            try:
                # Intensity Range をテキストボックスから取得
                int1 = float(self.yminTx.text())
                int2 = float(self.ymaxTx.text())
            except:
                # エラーメッセージを表示
                raise uGaoUtils.PlotException('Common', 'C027', ("TOF Range",))

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

        self.zMin = int1
        self.zMax = int2

        # マップを再描画
        self.PlotChart()

    #########################################
    def OnBt2dPlot(self, event=None):
        """
        2次元プロッタボタン押下イベント処理
        @param   event イベント情報
        @retval 無し
        """
        try:
            try:
                # PSD No. をテキストボックスから取得
                psd = int(self.psdTx0.text())
            except:
                # エラーメッセージを表示
                raise uGaoUtils.PlotException('Common', 'C026', ("PSD No.",))
            # PSD No. の入力範囲チェック
            if psd < 0 or psd > self.maxPSD:
                strPSD = "%d" % self.maxPSD
                raise uGaoUtils.PlotException('Common', 'C008', ("PSD", "0", strPSD))
            # 有効な検出器か
            if not self.do.IsValid(psd):
                # 無効な検出器
                raise uGaoUtils.PlotException('FastVis', 'F005', (self.psdTx0.text(),))

        except uGaoUtils.PlotException as ex:
            uGaoUtils.PlotMessage(self.frame, ex)
            return
        # PSD のデータを取得
        tof, zz, er = self.do.GetPSDArray(psd)

        if tof is None:
            return

        # 2次元マップ用のデータ作成
        x = arange(0, self.numPixel + 1, 1)
        # X, Y = meshgrid(x, tof)
        # map = (zz, er, X, Y)
        eca = Manyo.ElementContainerArray()
        for i in range(self.numPixel):
            XRANGE = Manyo.MakeDoubleVector()
            XRANGE.push_back(i - 0.5)
            XRANGE.push_back(i + 0.5)
            HH = Manyo.HeaderBase()
            HH.Add("XRANGE", XRANGE)
            EC = Manyo.ElementContainer(HH)
            II = []
            EE = []
            for k in range(len(zz)):
                II.append(float(zz[k][i]))
                EE.append(float(er[k][i]))
            EC.Add("TOF", tof)
            EC.Add("Intensity", II)
            EC.Add("Error", EE)
            EC.SetKeys("TOF", "Intensity", "Error")
            eca.Add(EC)

        # D2 プロッタが表示されているか
        if self.d2 is None:
            self.d2 = Plot2DMapOnDetectMap(100, self)
        self.d2.PlotMap(eca, ("Xaxis", "TOF"))
        # タイトルを取得
        runNo = self.do.GetTitle()
        main = "Run No.: " + runNo
        sub = "PSD No.: %d" % psd
        self.d2.SetTitles(main, sub)

    #########################################
    def OnBtSumm(self, event=None):
        """
        Summボタン押下イベント処理
        @param   event イベント情報
        @retval 無し
        """
        # 積算語にデータをプロッタへ追加
        self.AddToPlot(True)

    #########################################
    def AddToPlot(self, sumFlag):
        """
        Append Sum データをプロッタへ追加
        @param   sumFlag True であれば、積算後に追加
        @retval 無し
        """
        try:
            try:
                # 始点のPSD-ID をテキストボックスから取得
                psd1 = int(self.psdTx1.text())
                px1 = int(self.pxTx1.text())
                self.ppsd = psd1
            except:
                # エラーメッセージを表示
                raise uGaoUtils.PlotException(
                    'Common', 'C026', ("PSD No. and Pixel No."))

            try:
                # 終点のPSD-ID をテキストボックスから取得
                psd2 = int(self.psdTx2.text())
                px2 = int(self.pxTx2.text())
            except:
                # 終点が空白であれば、始点=終点とする
                psd2 = psd1
                px2 = px1
                # 終点テキストボックスをクリアする
                self.psdTx2.setText("")
                self.pxTx2.setText("")

            # PSD No. の入力範囲チェック
            if psd1 < 0 or psd1 > self.maxPSD or psd2 < 0 or psd2 > self.maxPSD:
                strPSD = "%d" % self.maxPSD
                raise uGaoUtils.PlotException('Common', 'C008', ("PSD", "0", strPSD))
            # Pixel No. の入力範囲チェック
            if px1 < 0 or px1 >= self.numPixel or px2 < 0 or px2 >= self.numPixel:
                strPx = "%d" % (self.numPixel - 1)
                raise uGaoUtils.PlotException('Common', 'C008', ("Pixel", "0", strPx))

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

        # 逆転していたなら
        if psd1 > psd2:
            # 入れ替え
            tmp = psd1
            psd1 = psd2
            psd2 = tmp
        # 逆転していたなら
        if px1 > px2:
            # 入れ替え
            tmp = px1
            px1 = px2
            px2 = tmp

        # PSD-ID を格納するリストを準備(積算用)
        lstSum = []
        # ヒストグラム を格納するリストを準備
        datList = []

        Xaxis = self.cbXax.currentIndex()
        # mu.UtsusemiMessage("Xaxis={}".format(Xaxis))
        if Xaxis == -1:
            Xaxis = 0
        if Xaxis == 0:
            # PSD No. の範囲を繰り返す
            while psd1 <= psd2:
                # 検出器は有効か
                if psd1 in self.hTable and self.do.IsValid(psd1):
                    px = px1
                    # ピクセル範囲を繰り返す
                    while px <= px2:
                        # 積算ならば
                        if sumFlag:
                            # 積算用PSD-ID リストに追加
                            lstSum.append((psd1, px))
                        else:
                            # 個別であれば、ヒストグラムをデータリストに追加
                            dat = self.do.GetHistogram(psd1, px)
                            datList.append(dat)

                        px += 1
                psd1 += 1
            try:
                # 範囲内に有効なPSDがなければ
                if len(datList) == 0 and len(lstSum) == 0:
                    raise uGaoUtils.PlotException('FastVis', 'F004', ())
            except uGaoUtils.PlotException as ex:
                uGaoUtils.PlotMessage(self.frame, ex)
                return

            # 積算ならば
            if sumFlag:
                # PSD-ID のリストを渡して積算データを取得
                dat = self.do.IntegrateHistogram(lstSum)
                datList.append(dat)

        elif Xaxis == 1:  # along PSD
            if sumFlag:
                psdRange = [psd1, psd2]
                pixRange = [px1, px2]
                datList.append(self.do.GetLineFromIntensities(
                    psdRange, pixRange, "PSD"))
            else:
                for p in range(px1, (px2 + 1)):
                    psdRange = [psd1, psd2]
                    pixRange = [p, p]
                    datList.append(self.do.GetLineFromIntensities(
                        psdRange, pixRange, "PSD"))

        elif Xaxis == 2:  # along Pixel
            if sumFlag:
                psdRange = [psd1, psd2]
                pixRange = [px1, px2]
                datList.append(self.do.GetLineFromIntensities(
                    psdRange, pixRange, "PIXEL"))
            else:
                for p in range(psd1, (psd2 + 1)):
                    psdRange = [p, p]
                    pixRange = [px1, px2]
                    datList.append(self.do.GetLineFromIntensities(
                        psdRange, pixRange, "PIXEL"))
        else:
            pass

        # 選択中のプロッタがあれば
        if self.plot is not None:
            # データ追加要求イベント発行
            uGaoUtils.IFEvtProp(self.plot[1]).NotifyEvent(self, "add", datList)
        else:
            self.psd1 = psd1
            # 選択中のプロッタが無い場合は、新規にプロッタを追加
            self.OnNewPlot(data=datList)

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

    def OnNewPlot(self, evt=None, data=None):
        """
        新規プロッタの作成
        カレントプロッタの切替
        @param  evt  イベント
        @param  data ヒストグラムデータ
        @retval 無し
        """
        # 選択中のプロッタがあれば
        if self.plot is not None:
            # プロッタのアイコン化要求イベント発行
            # uGaoUtils.IFEvtProp(self.plot[1]).NotifyEvent(self, "doiconize", True)
            pass

        # プロッタ番号をインクリメント
        self.order += 1

        # プロッタフレームクローズイベントのリスナー登録
        uGaoUtils.IFEvtProp(self.order).AddListner(
            'plotclose', self.OnNotifyPlotClose)
        # プロッタフレームアイコン化イベントのリスナー登録
        uGaoUtils.IFEvtProp(self.order).AddListner(
            'ploticonized', self.OnNotifyPlotIconized)

        # 新しいプロッタウィンドウを開く
        pi = FastPlot.PlotFrame(self, data, self.order)
        self.plot = [pi, self.order]

        # プロッタ一覧に加える文字列を作成
        strPlot = " Plotter(%d)" % self.order
        # プロッタリストに追加
        self.plots.append(self.plot)

        # タイトルを取得
        runNo = self.do.GetTitle()
        main = "Run No.: " + runNo
        # New Plotterボタンから呼び出されると dataはNoneとなるのでlen(data)はエラーとなる
        if data is None:
            pass
        elif len(data) == 1:
            head = data[0][3]
            if mu.UTSUSEMI_KEY_HEAD_PIXELPOSITION in head and mu.UTSUSEMI_KEY_HEAD_PIXELPOLARANGLES in head and mu.UTSUSEMI_KEY_HEAD_PIXELAZIMANGLES in head:
                pv = head[mu.UTSUSEMI_KEY_HEAD_PIXELPOSITION].split(",")
                pa = head[mu.UTSUSEMI_KEY_HEAD_PIXELPOLARANGLES].split(",")
                aa = head[mu.UTSUSEMI_KEY_HEAD_PIXELAZIMANGLES].split(",")
                L2 = math.sqrt(float(pv[0]) * float(pv[0]) + float(pv[1]) * float(pv[1]) + float(pv[2]) * float(pv[2]))
                main += ("   ( 2theta:{:8.3f} / Azim:{:8.3f} / L2:{:9.3f} )".format(
                    float(pa[0]), float(aa[0]), L2))
            else:
                if "SubTitle" in head:
                    main += ("  ( {} )".format(head["SubTitle"]))

        # タイトル設定コマンド送信
        uGaoUtils.IFEvtProp(self.order).NotifyEvent(self, "title", (main, "*"))

        # 表示リストに追加
        an_item = QtWidgets.QListWidgetItem()
        an_item.setText(strPlot)
        self.plist.addItem(an_item)

        # 追加したプロッタを選択状態にする
        self.plist.setCurrentItem(an_item)

    ##############################################
    def OnListSelect(self, evt=None):
        """
        リスト選択イベント処理関数
        カレントプロッタの切替
        @param  evt  イベント
        @retval 無し
        """
        # 選択消失イベントか 2009.05.07 Minakawa Add
        if len(self.plots) == 0:
            return

        # 選択中のプロッタがあれば
        if self.plot is not None:
            # プロッタのアイコン化要求イベント発行
            uGaoUtils.IFEvtProp(self.plot[1]).NotifyEvent(
                self, "doiconize", True)

        # 選択されたプロッタのインスタンスをカレントプロッタとする
        self.plot = self.plots[self.plist.currentRow()]
        # プロッタ復元要求イベント発行
        uGaoUtils.IFEvtProp(self.plot[1]).NotifyEvent(self, "doiconize", False)

    ##############################################
    def OnLog(self, evt=None):
        """
        ログメニューイベント処理関数
        ログとリニアのモード切替
        @param  evt 　イベント
        @retval 無し
        """
        # メニューのチェックを取得し、ログフラグに設定
        self.logFlag = self.MenuWidgets['logscale'].isChecked()

        if self.logFlag:
            if self.zMin <= 0.0:
                self.zMin = 1.0E-20
        else:
            self.zMin = self.datMin

        self.yminTx.setText(("%g" % self.zMin))

        # マップの再描画
        self.PlotChart()

    ##############################################
    def OnSlider(self, evt):
        """
        スライダーイベント処理関数
        Z 軸範囲を変更(拡大)
        @param  evt  イベント
        @retval 無し
        """
        # スライダーの最大値を取得
        max = self.slider.maximum()

        # 現在値を取得し、割合に変換
        # svalue = max - self.slider.GetValue() + 1
        svalue = self.slider.value()

        # ログスケールか
        if self.logFlag:
            if self.datMin <= 0.0:
                self.zMin = 1.0E-20
            self.zMax = (self.datMax - self.zMin) * svalue / max

        # リニアスケール
        else:
            self.zMax = (self.datMax - self.datMin) * svalue / max

        # マップの再描画
        self.yminTx.setText(("%d" % self.zMin))
        self.ymaxTx.setText(("%d" % self.zMax))
        self.PlotChart()

    #####################################################
    def OnNotifyPlotClose(self, wid, evt, value=None):
        """
        プロッタクローズイベント対応処理
        @param  wid  イベント発生元のインスタンス
        @param  evt  イベントの種類
        @param  value  無し
        @retval 無し
        """
        i = 0
        # インスタンスリストから、クローズしたプロッタを検索
        for pl in self.plots:
            if pl[0] == wid:
                # I/F をクリア
                uGaoUtils.IFEvtProp(i + 1).InitIF()
                # インスタンスリストから削除
                del self.plots[i]
                # 表示リストから削除
                self.plist.clear()
                for pl2 in self.plots:
                    # プロッタ一覧に加える文字列を作成
                    an_item = QtWidgets.QListWidgetItem()
                    an_item.setText(" Plotter(%d)" % pl[1])
                    self.plist.addItem(an_item)
                # クローズしたプロッタがカレントだったなら
                if wid == self.plot[0]:

                    # カレントプロッタを変更
                    pno = len(self.plots)
                    # 他に開いているプロッタがあれば
                    if pno > 0:
                        # リストの最後のプロッタをカレントプロッタとする
                        self.plot = self.plots[pno - 1]
                        last_item = self.plist.item(pno - 1)
                        self.plist.setCurrentItem(last_item)
                        # プロッタ復元要求イベント発行
                        uGaoUtils.IFEvtProp(self.plot[1]).NotifyEvent(
                            self, "doiconize", False)
                    # 全部のプロッタがクローズされたなら
                    else:
                        # カレントプロッタ無しとする
                        self.plot = None

                break
            i += 1

    ###################################################
    def OnNotifyPlotIconized(self, wid, evt, flag):
        """
        プロッタアイコン化イベント対応処理
        @param  wid 　 イベント発生元のインスタンス
        @param  evt 　イベントの種類
        @param  flag  アイコン化フラグ
        @retval 無し
        """
        i = 0
        # インスタンスリストから、イベントの発生したプロッタを検索
        for pl in self.plots:
            # イベント発生元と同じインスタンスか
            if pl[0] == wid:
                strP = self.plist.itemFromIndex(i).text()
                # アイコン化されたのか
                if flag:
                    # 表示中のリストにアイコン化文字列を付加
                    self.plist.itemFromIndex(i).setText(strP + " - Iconized")
                # 復元であれば
                else:
                    # アイコン化文字列を消す
                    self.plist.itemFromIndex(i).setText(strP[:-11])
                break
            i += 1

    ######################################################
    def OnPrint(self, *args):
        """
        Print メニューイベント処理
        print コマンド処理
        印刷ダイアログを表示
        @param  *args イベント情報
        @retval 無し
        """
        # プラットフォームを判定
        if os.name == "posix" or os.name == "mac":
            # Linux または Mac OS なら
            # 現在の時間よりテンポラリファイル名を作成
            tsec = int(time.time() * 10.0)
            tmpf = "map%d.ps" % tsec

            try:
                try:
                    # ポストスクリプトファイルを作成
                    self.canvas.print_figure(
                        tmpf, dpi=600, orientation="landscape")
                except:
                    raise uGaoUtils.PlotException('Common', 'C018', (tmpf,))
                # スプーラにファイルを送信
                strCmd = "lpr %s" % tmpf
                ret = os.system(strCmd)
                # エラーがあったなら

                if ret > 0:
                    # エラーメッセージを表示
                    raise uGaoUtils.PlotException('Common', 'C023', ())

            except uGaoUtils.PlotException as ex:
                uGaoUtils.PlotMessage(self.frame, ex)

        # Linux または Mac OS 以外(Windowsを想定)
        # [inamura 210129] save figure file instead of use of printer because not sure about printing way on Windows...
        else:
            filepath, filt = QtWidgets.QFileDialog.getSaveFileName(
                self, u"Save the image ...", os.getcwd(), filter=u"(*.png *.eps)")
            filepath = str(filepath)
            if filepath == "":
                return
            file_name = os.path.basename(filepath)
            dir_name = os.path.dirname(filepath)
            base, extcode = os.path.splitext(file_name)
            print("#[inamura 210129] base, extcode=" + base + ", " + extcode)
            if extcode == ".png" or extcode == ".eps":
                uGaoUtils.IFEvtProp(0).NotifyEvent(self, "save", filepath)
            else:
                raise UserWarning("File name must be type of .png or .eps")
            """
            # 現在の解像度を取得
            dpi = self.canvas.figure.dpi.get()
            # 印刷用に解像度を高くする
            self.canvas.figure.dpi.set(160)
            #用紙をA4に設定
            self.canvas.printerData.SetPaperId(wx.PAPER_A4)
            # 用紙を横方向に設定
            self.canvas.printerData.SetOrientation(wx.LANDSCAPE)
            # マージンを設定
            self.canvas.printer_margin = 1.0
            # 印刷ダイアログを表示

            self.canvas.Printer_Print()
            # 解像度を元に戻す
            self.canvas.figure.dpi.set(dpi)
            # 再描画
            self.canvas.draw()
            """

    ######################################################
    def OnSave(self, wid, evt, fname):
        """
        画像保存処理
        @param  wid イベント発生元のインスタンス
        @param  evt イベントタイプ
        @param  fname ファイル名
        @retval 無し
        """
        try:
            try:
                self.canvas.print_figure(fname, dpi=300)
            except:
                raise uGaoUtils.PlotException('Common', 'C018', (fname,))
        except uGaoUtils.PlotException as ex:
            uGaoUtils.PlotMessage(self.parent.frame, ex)

    #########################################
    # def OnNotify2DClose(self, *args):
        """
        2Dプロッタクローズイベント受信処理
        @param *args イベント
        @retval 無し
        """
        # I/F をクリア
        # uGaoUtils.IFEvtProp(100).InitIF()
        # if self.d2 is not None:
        #    del self.d2
        # self.d2 = None

    ##############################################
    def OnClose(self, evt=None):
        """
        デテクタマップ画面クローズ
        @param  evt 　イベント
        @retval 無し
        """
        # 印刷に使用したテンポラリファイルのリストを取得
        tmpfiles = glob.glob("map*.ps")
        # テンポラリファイルを削除
        for tmpf in tmpfiles:
            try:
                # ファイルを削除
                os.remove(tmpf)
            except:
                # 削除 できなかったら(スプーラが使用中?)、次回に削除
                pass
        # メッセージの解放
        uGaoUtils.MessageFile().CloseDoc()

        # ---> 2008/04/27 Minakawa add
        # 全 1D Plotter のI/F をクリア
        for i in range(len(self.plots)):
            uGaoUtils.IFEvtProp(i + 1).InitIF()
        # 2Dプロッタが開いているなら
        if self.d2 is not None:
            # 2Dプロッタのクローズ要求を出す(孫画面のI/F をクリアさせるため)　
            self.d2.Request2DClose()
        # 自分のI/F をクリア
        uGaoUtils.IFEvtProp(0).InitIF()
        # <--- 2008/04/27 Minakawa add

        # 検出器マップ画面のクローズ
        self.close()

        del self.do.matrix
        del self.do.searchId
        del self.do
    ##############################################

    def closeEvent(self, evt):
        """
        Close Event (virtual)
        """
        self.OnClose()
        evt.accept()

#######################################
#  DetectMap
#######################################


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

    def __init__(self, matrix=None, isPixelReverse=False):
        """
        アプリケーションスタート
        @param  matrix  エレメントコンテナマトリックス
        @retval 無し
        """
        # 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(10)
        app.setFont(deffont)
        try:
            if matrix is None:
                raise uGaoUtils.PlotException('FastVis', 'F002', ())

        except uGaoUtils.PlotException as ex:
            uGaoUtils.PlotMessage(None, ex)
        else:

            fig = MapFrame(None, matrix, isPixelReverse)
            if PYSIDEVER == 6:
                app.exec()
            else:
                app.exec_()


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


#######################################
#  Plot2DMapOnDetectMap
#######################################
class Plot2DMapOnDetectMap(object):
    """
    2次元マップ表示クラス
    """
    #########################################################

    def __init__(self, pno, frame):
        """
        コンストラクタ
        @param  pno  process no
        @param  frame  親フレーム
        @retval 無し
        """
        self.frame = frame
        self.d2 = None
        self.pno = pno
        self.ifi = uGaoUtils.IFEvtProp(pno)

    #########################################
    def PlotMap(self, map_obj, _keys):
        """
        2D マップ表示
        @param map　2D データ
        @param _keys key for uGao plotting
        @retval 無し
        """
        # D2 プロッタが表示されているか
        if self.d2 is None:
            self.ifi.AddListner('u2dclosed', self.OnNotify2DClose)
            self.d2 = D2Vis.M2PlotPlus(self.frame, None, self.pno)

        self.ifi.NotifyEvent(self, "changedata", map_obj)
        self.ifi.NotifyEvent(self, "turnmodeplotonly", True)
        self.ifi.NotifyEvent(self, 'setwindowtitle', ["VisualContM_Slice"])

        while(True):
            time.sleep(0.1)
            if self.ifi.GetProperty("isready"):
                break
        xtitle, ytitle = _keys
        if xtitle == "":
            xtitle = "-"
        if ytitle == "":
            ytitle = "-"
        self.ifi.NotifyEvent(self, "showdata", (xtitle, ytitle, "-", 0.0, 0.0))

    #########################################
    def SetTitles(self, main_t, sub_t):
        # タイトル設定コマンド送信
        self.ifi.NotifyEvent(self, "settitles", (main_t, sub_t))

    #########################################
    def OnNotify2DClose(self, *args):
        """
        2Dプロッタクローズイベント受信処理
        @param evt　　イベント
        @retval 無し
        """
        # print "OnNotify2DClose"
        self.plotNow = None
        if self.d2 is not None:
            del self.d2
            self.d2 = None
        self.ifi.InitIF()

    #########################################
    def Request2DClose(self):
        """
        2Dプロッタクローズ要求処理
        @param  無し
        @retval 無し
        """
        # 2Dプロッタが開いているなら
        if self.d2 is not None:
            # 2Dプロッタのクローズ要求を出す　
            self.ifi.NotifyEvent(self, "close")
