コード例 #1
0
    def mianThread_callbacklog(self, df):
        mpl = StockMplCanvas(self, width=5, height=4, dpi=100)
        mpl.start_staict_plot(df)
        mpl_ntb = NavigationToolbar(mpl, self)
        mpl_ntb.setStyleSheet("background-color:white;color:black")

        self.grid.addWidget(mpl, 2, 0, 12, 12)
        self.grid.addWidget(mpl_ntb, 2, 0, 1, 5)
コード例 #2
0
    def kLineThread_callbacklog(self, df):
        self.df = df
        self.mplK = KMplCanvas(self, width=5, height=4, dpi=100)
        self.mplK.start_staict_plot(df)
        mpl_ntb = NavigationToolbar(self.mplK, self)
        mpl_ntb.setStyleSheet("background-color:white;color:black")

        self.grid_k.addWidget(self.mplK, 2, 0, 13, 12)
        self.grid_k.addWidget(mpl_ntb, 2, 0, 1, 5)
コード例 #3
0
ファイル: MusicPlot.py プロジェクト: T-13/DroneTasks
class MusicPlot(FigureCanvas):
    def __init__(self):
        # Init stuff
        self.fig = Figure(tight_layout=True)
        self.axes = self.fig.add_subplot(111)

        FigureCanvas.__init__(self, self.fig)
        self.plotnav = NavigationToolbar(self.fig.canvas, self)
        self.plotnav.setStyleSheet("QToolBar { border: 0px }")
        self.plotnav.setOrientation(Qt.Vertical)
        self.plotnav.setMaximumWidth(35)

        self.background = None
        self.line = None
        self.xmin = 0
        self.xmax = 0

    def plot(self, data_time, data_samples, step):
        # Plot data
        self.axes.margins(x=0)
        self.axes.plot(data_time[0::step], data_samples[0::step], "-")
        self.fig.canvas.draw()

        self.xmin, self.xmax = self.axes.get_xlim()

    def drawHLine(self, X):
        if self.background:
            if self.xmax < X:
                X = self.xmax
            self.line.set_xdata([X])
            self.fig.canvas.restore_region(self.background)
            self.axes.draw_artist(self.line)
            self.fig.canvas.blit(self.fig.bbox)

    def initBackground(self):
        self.background = self.fig.canvas.copy_from_bbox(self.fig.bbox)
        if not self.line:
            self.line = self.axes.axvline(0, color='r')

    def addLabel(self, text, label):
        if label == "X" or label == "x":
            self.axes.set_xlabel(text)
        elif label == "Y" or label == "y":
            self.axes.set_ylabel(text)
コード例 #4
0
class MainWindow(QWidget):
    sig_sound_play_at = pyqtSignal(float)
    sig_sound_pause = pyqtSignal()
    sig_sound_stop = pyqtSignal()
    sig_record_stop = pyqtSignal()

    def __init__(self):
        super().__init__()

        self.sound = None
        self.signal = None

        self.plotbackground = None

        self.playing = False
        self.sound_paused = False
        self.sound_start_at = 0

        self.recording = False
        self.doppler = False  # Doppler simulation

        self.initUI()

    def initUI(self):
        spacer = QSpacerItem(50, 0, QSizePolicy.Minimum)

        # File selector
        lbl_file = QLabel("File:")
        self.txt_file = QLineEdit()
        self.txt_file.setPlaceholderText("Select file ...")
        btn_file = QPushButton("Select")
        btn_file.clicked.connect(self.show_open_dialog)

        # Save
        self.btn_save = QPushButton("Save")
        self.btn_save.setDisabled(True)
        self.btn_save.clicked.connect(self.show_save_dialog)

        # Audio controls
        self.btn_pause = QPushButton("Pause")
        self.btn_pause.setDisabled(True)
        self.btn_pause.clicked.connect(self.sound_pause)
        self.sound_mutex = QMutex()
        self.sound_pause_cond = QWaitCondition()

        self.btn_play = QPushButton("Play")
        self.btn_play.setDisabled(True)
        self.btn_play.clicked.connect(self.sound_play)

        self.btn_stop = QPushButton("Stop")
        self.btn_stop.setDisabled(True)
        self.btn_stop.clicked.connect(self.sound_stop)

        # Doppler Shift simulation
        self.cb_source_speed = QComboBox()
        self.cb_source_speed.setToolTip("Source speed")
        self.cb_source_speed.addItems(
            ["20 km/h", "50 km/h", "100 km/h", "150 km/h", "200 km/h"])
        self.cb_source_speed.setCurrentIndex(2)
        self.source_speeds = [5.56, 13.89, 27.78, 41.67,
                              55.56]  # Same indexes as text above (in m/s)
        self.btn_doppler = QPushButton("Simulate Doppler")
        self.btn_doppler.setToolTip("Apply simple Doppler Shift simulation")
        self.btn_doppler.setDisabled(True)
        self.btn_doppler.clicked.connect(self.doppler_simulate)

        # Effects
        self.cb_effect = QComboBox()
        self.cb_effect.setToolTip("Preset effects")
        self.cb_effect.setMaximumWidth(150)
        self.effects = []
        for root, dirs, files in os.walk("resources/impulses"):
            for file in files:
                if file.endswith(".wav") or file.endswith(".mp3"):
                    self.cb_effect.addItem(file.split(".")[0])
                    self.effects.append(os.path.join(root, file))
        self.btn_effect = QPushButton("Apply Effect")
        self.btn_effect.clicked.connect(self.effect_apply)
        self.btn_effect_load = QPushButton("Load Effect")
        self.btn_effect_load.clicked.connect(self.effect_load)

        # Recording
        self.cb_sample_rate = QComboBox()
        self.cb_sample_rate.setToolTip("Sampling rate")
        self.cb_sample_rate.addItems(
            ["8.000 Hz", "11.025 Hz", "22.050 Hz", "44.100 Hz"])
        self.cb_sample_rate.setCurrentIndex(3)
        self.sampling_rates = [8000, 11025, 22050,
                               44100]  # Same indexes as text above
        self.btn_record = QPushButton("Record")
        self.btn_record.setMinimumWidth(100)
        self.btn_record.clicked.connect(self.record)

        self.cb_bit_depth = QComboBox()
        self.cb_bit_depth.setToolTip("Bit depth")
        self.cb_bit_depth.addItems(["8 b", "16 b"])
        self.cb_bit_depth.setCurrentIndex(1)
        self.bit_depths = [pyaudio.paUInt8,
                           pyaudio.paInt16]  # Same indexes as text above

        # Analysis (ST-DFT)
        self.stdft_window = QLineEdit()
        self.stdft_window.setText("256")
        self.stdft_window.setToolTip("Window length")
        self.stdft_window.setMaximumWidth(35)
        self.stdft_window.setValidator(QIntValidator(0, 2147483647))
        self.stdft_noverlap = QLineEdit()
        self.stdft_noverlap.setText("128")
        self.stdft_noverlap.setMaximumWidth(35)
        self.stdft_noverlap.setValidator(QIntValidator(0, 2147483647))
        self.stdft_noverlap.setToolTip(
            "Overlap between windows (must be smaller than window length)")
        self.btn_analyse = QPushButton("Analyse")
        self.btn_analyse.setToolTip(
            "Perform Short Time Discrete Fourier Transform analysis (spectrogram)"
        )
        self.btn_analyse.setDisabled(True)
        self.btn_analyse.clicked.connect(lambda: self.analyse())

        # Filter
        self.filter_order = QLineEdit()
        self.filter_order.setText("5")
        self.filter_order.setToolTip("Filter order")
        self.filter_order.setMaximumWidth(25)
        self.filter_order.setValidator(QIntValidator(0, 100))
        self.filter_cut_low = QLineEdit()
        self.filter_cut_low.setText("500")
        self.filter_cut_low.setToolTip("Low critical frequency")
        self.filter_cut_low.setMaximumWidth(35)
        self.filter_cut_low.setValidator(QIntValidator(0, 2147483647))
        self.filter_cut_high = QLineEdit()
        self.filter_cut_high.setText("5000")
        self.filter_cut_high.setToolTip("High critical frequency")
        self.filter_cut_high.setMaximumWidth(35)
        self.filter_cut_high.setValidator(QIntValidator(0, 2147483647))
        self.btn_filter = QPushButton("Filter")
        self.btn_filter.setToolTip("Filter frequencies")
        self.btn_filter.setDisabled(True)
        self.btn_filter.clicked.connect(self.filter)

        # Graph space
        self.figure = Figure()
        FigureCanvas(self.figure)
        self.figure.canvas.setMinimumHeight(400)
        self.figure.canvas.mpl_connect("button_press_event",
                                       self.on_plot_click)
        self.figure.canvas.mpl_connect("motion_notify_event",
                                       self.on_plot_over)

        # Graph toolbar
        self.plotnav = NavigationToolbar(self.figure.canvas,
                                         self.figure.canvas)
        self.plotnav.setStyleSheet("QToolBar { border: 0px }")
        self.plotnav.setOrientation(Qt.Vertical)

        # Layout
        hbox_top = QHBoxLayout()
        hbox_top.addWidget(lbl_file)
        hbox_top.addWidget(self.txt_file)
        hbox_top.addWidget(btn_file)
        hbox_top.addWidget(self.btn_save)
        hbox_top.addStretch()
        hbox_top.addSpacerItem(spacer)
        hbox_top.addWidget(self.btn_pause)
        hbox_top.addWidget(self.btn_play)
        hbox_top.addWidget(self.btn_stop)

        hbox_bot = QHBoxLayout()
        hbox_bot.addWidget(self.cb_source_speed)
        hbox_bot.addWidget(self.btn_doppler)
        hbox_bot.addStretch()
        hbox_bot.addSpacerItem(spacer)
        hbox_bot.addWidget(self.cb_effect)
        hbox_bot.addWidget(self.btn_effect)
        hbox_bot.addWidget(self.btn_effect_load)
        hbox_bot.addStretch()
        hbox_bot.addSpacerItem(spacer)
        hbox_bot.addWidget(self.cb_sample_rate)
        hbox_bot.addWidget(self.cb_bit_depth)
        hbox_bot.addWidget(self.btn_record)

        hbox_bot2 = QHBoxLayout()
        hbox_bot2.addWidget(self.stdft_window)
        hbox_bot2.addWidget(self.stdft_noverlap)
        hbox_bot2.addWidget(self.btn_analyse)
        hbox_bot2.addStretch()
        hbox_bot2.addWidget(self.filter_order)
        hbox_bot2.addWidget(self.filter_cut_low)
        hbox_bot2.addWidget(self.filter_cut_high)
        hbox_bot2.addWidget(self.btn_filter)

        vbox = QVBoxLayout()
        vbox.addLayout(hbox_top)
        vbox.addWidget(self.figure.canvas)
        vbox.addLayout(hbox_bot)
        vbox.addLayout(hbox_bot2)

        # Window
        self.setLayout(vbox)
        self.setGeometry(300, 300, 1000, 500)
        self.setWindowTitle("Signal Processor - Sound")
        self.show()

    # Overriden resize event
    def resizeEvent(self, resizeEvent):
        if self.is_sound_loaded():
            self.on_plot_change(None)
        self.plotnav.move(self.width() - 55, 0)

    def update_ui(self):
        block_general = self.playing or self.sound_paused or self.recording

        self.btn_save.setDisabled(not self.is_sound_loaded())

        self.btn_pause.setDisabled(not self.playing)
        self.btn_pause.setText("Resume" if self.sound_paused else "Pause")
        self.btn_play.setDisabled(self.playing or self.recording)
        self.btn_stop.setDisabled(not self.playing or self.recording)

        self.plotnav.setDisabled(self.playing and not self.sound_paused)

        self.btn_doppler.setDisabled(not self.is_sound_loaded()
                                     or self.doppler)

        self.btn_effect.setDisabled(block_general)
        self.btn_effect_load.setDisabled(block_general)

        self.btn_record.setDisabled(self.playing or self.sound_paused)
        self.btn_record.setText(
            "Stop Recording" if self.recording else "Record")

        self.btn_analyse.setDisabled(block_general)
        self.btn_filter.setDisabled(block_general)

    def show_open_dialog(self):
        fname = QFileDialog.getOpenFileName(self,
                                            "Open file",
                                            filter="Audio (*.wav *.mp3)")
        if fname[0] and self.load_sound(fname[0]):
            self.txt_file.setText(fname[0])

    def show_save_dialog(self):
        fname = QFileDialog.getSaveFileName(self,
                                            "Save file",
                                            filter="Audio (*.wav *.mp3)")
        if fname[0] and self.is_sound_loaded():
            ext = fname[0].rsplit(".", 1)[-1]
            try:
                self.sound.export(fname[0], format=ext)
            except exceptions.CouldntEncodeError:
                print("Failed to save signal!")
            else:
                self.txt_file.setText(fname[0])

    def load_sound(self, file):
        self.sound_stop()
        self.doppler = False

        try:
            self.sound = AudioSegment.from_file(file)
            self.signal = np.array(self.sound.get_array_of_samples())
        except exceptions.CouldntDecodeError:
            print("Failed to load sound!")
            self.sound = None
            self.signal = None
            return False
        else:
            self.update_ui()
            self.plot(self.signal, self.sound)
            return True

    def is_sound_loaded(self):
        return self.sound is not None and self.signal is not None

    def load_signal(self, data, sample_width, rate, channels):
        self.sound = AudioSegment(
            data=data,
            sample_width=sample_width,  # 3 (24-bit) not supported by pydub
            frame_rate=rate,
            channels=channels)
        self.signal = np.array(self.sound.get_array_of_samples())
        self.update_ui()
        self.plot(self.signal, self.sound)

    def effect_load(self):
        feffect = self.effects[self.cb_effect.currentIndex()]
        if self.load_sound(feffect):
            self.txt_file.setText(feffect)
            self.plot(self.signal, self.sound)

    def effect_apply(self):
        if not self.is_sound_loaded():
            print("Failed to apply effect! No sound loaded!")
            return

        if self.sound.channels > 2:
            print("Failed to apply effect! Sound has more than 2 channels!")
            return

        feffect = self.effects[self.cb_effect.currentIndex()]
        try:
            effect_sound = AudioSegment.from_file(feffect)
            effect_signal = np.array(effect_sound.get_array_of_samples())
        except exceptions.CouldntDecodeError:
            print("Failed to load effect!")

        if effect_sound.frame_rate != self.sound.frame_rate:
            print(
                "Failed to apply effect! Effect rate ({}) not same as sound rate ({})!"
                .format(effect_sound.frame_rate, self.sound.frame_rate))
            return

        # Create stereo in case original sound is mono
        sound_channels = self.sound.channels
        if self.sound.channels < 2:
            self.sound = AudioSegment.from_mono_audiosegments(
                self.sound, self.sound)
            self.signal = np.array(self.sound.get_array_of_samples())

        # Convolve signals using fast fourier transform (into stereo, each channel separately)
        step = effect_sound.channels
        left = None
        right = None

        for i in range(0, sound_channels):
            if MANUAL_CONVOLVE:
                # Manual convolve
                n = fftpack.helper.next_fast_len(
                    len(self.signal[i::step]) + len(effect_signal[i::step]) -
                    1)
                x = np.fft.rfft(
                    np.append(self.signal[i::step],
                              np.zeros(len(effect_signal[i::step]) - 1)), n)
                y = np.fft.rfft(
                    np.append(effect_signal[i::step],
                              np.zeros(len(self.signal[i::step]) - 1)), n)
                ch = np.fft.irfft(x * y)
            else:
                # SciPy fftconvolve
                ch = signal.fftconvolve(self.signal[i::step],
                                        effect_signal[i::step])

            # Normalize and amplify
            ch = np.array(ch / np.linalg.norm(ch))
            ch = np.multiply(ch, 65535)  # float to int
            volume_diff = np.max(self.signal[i::step]) / np.max(ch)
            ch = np.multiply(ch, volume_diff)

            if i == 0:
                left = ch
                if sound_channels == 1:
                    right = left  # Mono input, copy channel
            else:
                right = ch

        # Join channels back together and load signal
        final = np.empty(left.size + right.size, np.int16)
        final[0::step] = left.astype(np.int16)
        final[1::step] = right.astype(np.int16)
        self.load_signal(b''.join(final), 2, self.sound.frame_rate,
                         effect_sound.channels)

    def doppler_simulate(self):
        self.doppler = True
        self.update_ui()

        speed_source = self.source_speeds[self.cb_source_speed.currentIndex()]

        # Frequency manipulation
        speed_sound = constants.speed_of_sound
        freq_in = speed_sound / (speed_sound -
                                 speed_source) * self.sound.frame_rate
        freq_out = speed_sound / (speed_sound +
                                  speed_source) * self.sound.frame_rate

        half1 = self.sound[0:int(len(self.sound) * 0.5)]
        half1 = AudioSegment(data=half1.get_array_of_samples(),
                             sample_width=self.sound.sample_width,
                             frame_rate=int(freq_in),
                             channels=self.sound.channels)

        half2 = self.sound[int(len(self.sound) * 0.5):]
        half2 = AudioSegment(data=half2.get_array_of_samples(),
                             sample_width=self.sound.sample_width,
                             frame_rate=int(freq_out),
                             channels=self.sound.channels)

        self.sound = half1.append(half2, crossfade=100)
        self.signal = np.array(self.sound.get_array_of_samples())

        # Volume manipulation (decrease with distance)
        half_time = half1.duration_seconds
        dist_max = speed_source * half_time
        print("Maximum distance: {} m".format(dist_max))

        distances = np.linspace(
            0.0,
            speed_source *
            (len(self.signal) / self.sound.frame_rate / self.sound.channels),
            num=int(len(self.signal) / self.sound.channels))  # Plot distances
        distances -= dist_max  # Take away maximum distance to get relative from center
        distances = np.absolute(
            distances)  # Make positive in both directions (_/^\_)
        distances = np.maximum(distances, 1.0)  # Prevent center clipping

        new_volumes = np.power(distances, -1.0)  # Scale volume with distance
        for i in range(0, self.sound.channels):  # Apply to all channels
            self.signal[i::self.sound.channels] = np.multiply(
                self.signal[i::self.sound.channels], new_volumes)

        self.signal = self.signal.astype(np.int16)

        # Load and plot new signal with doppler and visualization subplot
        self.load_signal(b''.join(self.signal), self.sound.sample_width,
                         self.sound.frame_rate, self.sound.channels)
        self.plot(self.signal, self.sound, doppler_max=half_time)

    def analyse(self,
                filter_w=[],
                filter_h=[],
                filter_cl=-1.0,
                filter_ch=-1.0):
        if not self.stdft_window.text() or not self.stdft_noverlap.text():
            print("Failed to analyse! Invalid input (must be integers)!")
            return

        window = int(self.stdft_window.text())
        noverlap = int(self.stdft_noverlap.text())

        if window <= 0 or noverlap <= 0:
            print(
                "Failed to analyse! Invalid input (must be integers greater than 0)!"
            )
            return

        if noverlap >= window:
            print("Failed to analyse! Overlap must be less than window size!")
            return

        if self.sound.channels > 1:
            print("Warning! Analysing only first channel!")

        self.plot(self.signal,
                  self.sound,
                  stdft_window=window,
                  stdft_noverlap=noverlap,
                  filter_w=filter_w,
                  filter_h=filter_h,
                  filter_cl=filter_cl,
                  filter_ch=filter_ch)

    def filter(self):
        if not self.filter_order.text() or not self.filter_cut_low.text(
        ) or not self.filter_cut_high.text():
            print("Failed to filter! Invalid input (must be integers)!")
            return

        order = int(self.filter_order.text())
        cut_low = int(self.filter_cut_low.text())
        cut_high = int(self.filter_cut_high.text())

        if order < 0 or cut_low < 0 or cut_high < 0:
            print(
                "Failed to filter! Invalid input (must be integers greater or equal 0)!"
            )
            return

        # Normalize critical frequencies (Nyquist as 1)
        cut_low = cut_low / (self.sound.frame_rate * 0.5)
        cut_high = cut_high / (self.sound.frame_rate * 0.5)

        # Design filter
        b, a = signal.butter(order, [cut_low, cut_high], "bandstop")
        w, h = signal.freqz(b, a)

        # Filter each channel
        for i in range(0, self.sound.channels):
            x = np.array(self.signal[i::self.sound.channels])  # Original
            y = np.zeros(len(x))  # Filtered

            if MANUAL_FILTER:
                # Manual filter
                for n in range(len(x)):
                    y[n] = 0
                    for k in range(len(b)):
                        if n - k >= 0:
                            y[n] = y[n] + b[k] * x[n - k]

                    for k in range(1, len(a)):
                        if n - k >= 0:
                            y[n] = y[n] - a[k] * y[n - k]

                if MANUAL_FILTER_TEST:
                    y_sp = signal.lfilter(b, a, x)
                    if np.allclose(y, y_sp, rtol=1e-02, atol=1e-08):
                        print("Manual filter test passed!")
                    else:
                        print("Manual filter test failed!")
            else:
                # SciPy lfilter
                y = signal.lfilter(b, a, x)

            self.signal[i::self.sound.channels] = y

        # Load and analyse filtered signal
        self.load_signal(b''.join(self.signal), self.sound.sample_width,
                         self.sound.frame_rate, self.sound.channels)
        self.analyse(filter_w=w,
                     filter_h=h,
                     filter_cl=cut_low,
                     filter_ch=cut_high)

    def plot(self,
             sig,
             sound,
             doppler_max=-1.0,
             stdft_window=-1,
             stdft_noverlap=-1,
             filter_w=[],
             filter_h=[],
             filter_cl=-1.0,
             filter_ch=-1.0):
        self.figure.clear()
        self.subplots = []
        self.lclick = []
        self.lclick_pos = 0
        self.lover = []
        self.lover_pos = 0
        self.lframe = []
        self.lframe_pos = 0
        self.sound_start_at = 0

        doppler = doppler_max != -1.0
        analysis = stdft_window != -1 and stdft_noverlap != -1
        filter_fr = len(filter_w) != 0 and len(
            filter_h) != 0 and filter_cl != -1.0 and filter_ch != -1.0
        subplots = sound.channels + doppler + analysis + filter_fr + (
            1 if filter_fr else 0)

        # X axis as time in seconds
        time = np.linspace(0, sound.duration_seconds, num=len(sig))

        for i in range(0, sound.channels):
            ax = self.figure.add_subplot(subplots, 1, i + 1)

            # Plot current channel, slicing it away
            ax.plot(time[i::sound.channels],
                    sig[i::sound.channels])  # [samp1L, samp1R, samp2L, samp2R]
            ax.margins(0)

            # Hide X axis on all but last channel
            if i + 1 < subplots - filter_fr:
                ax.get_xaxis().set_visible(False)
            # Display Y label somewhere in the middle
            if i == max(int(sound.channels / 2) - 1, 0):
                ax.set_ylabel("Amplitude")

            self.subplots.append(ax)

        if doppler:
            ax = self.figure.add_subplot(subplots, 1,
                                         sound.channels + analysis + 1)
            ax.margins(0)
            ax.plot(time, sig * [0])
            ax.axhline(0, linewidth=2, color="black")
            ax.axvline(doppler_max,
                       ymin=0.25,
                       ymax=0.75,
                       linewidth=2,
                       color="blue")
            ax.set_ylim([-1, 1])
            ax.get_yaxis().set_ticks([])
            ax.set_ylabel("Doppler Sim")
            self.subplots.append(ax)

        if analysis:
            ax = self.figure.add_subplot(subplots, 1,
                                         sound.channels + doppler + 1)
            ax.margins(0)
            ax.specgram(sig[0::sound.channels],
                        Fs=self.sound.frame_rate,
                        NFFT=stdft_window,
                        noverlap=stdft_noverlap)
            ax.set_ylabel("Freq (Hz)")
            self.subplots.append(ax)

        self.figure.subplots_adjust(hspace=0.0)
        ax.set_xlabel("Time (s)")

        if filter_fr:
            ax = self.figure.add_subplot(subplots, 1,
                                         sound.channels + analysis + 2)
            ax.margins(0, 0.1)
            ax.plot(filter_w / np.pi * self.sound.frame_rate * 0.5,
                    abs(filter_h) * max(sig[0::sound.channels]))
            ax.set_xlabel("Frequency (Hz)")
            ax.set_ylabel("Amplitude")
            ax.axvline(filter_cl, color="green")  # Cutoff frequency start
            ax.axvline(filter_ch, color="green")  # Cutoff frequency stop
            self.subplots.append(ax)

        # Handle zoom/pan events
        for ax in self.subplots:
            ax.callbacks.connect("xlim_changed", self.on_plot_change)
            ax.callbacks.connect("ylim_changed", self.on_plot_change)

        self.figure.canvas.draw()

        # Save background for updating on the fly
        self.plotbackground = self.figure.canvas.copy_from_bbox(
            self.figure.bbox)

        # Create lines (for later use, hidden until first update)
        for ax in self.subplots:
            line = ax.axvline(0, linewidth=1, color="black")
            self.lclick.append(line)
            line = ax.axvline(0, linewidth=1, color="grey")
            self.lover.append(line)
            line = ax.axvline(0, linewidth=1, color="blue")
            self.lframe.append(line)

    def on_plot_change(self, axes):
        # Hide all lines to not save them as part of background
        for line in itertools.chain(self.lclick, self.lover, self.lframe):
            line.set_visible(False)

        # Redraw and resave new layout background
        self.figure.canvas.draw()
        self.plotbackground = self.figure.canvas.copy_from_bbox(
            self.figure.bbox)

        # Reshow all lines
        for line in itertools.chain(self.lclick, self.lover, self.lframe):
            line.set_visible(True)

    def is_plotnav_active(self):
        return self.plotnav._active is None

    def on_plot_click(self, event):
        if not self.is_plotnav_active():
            return

        if event.xdata is not None and event.ydata is not None:
            self.sound_start_at = event.xdata
            self.sound_play()
            self.update_ui()

            # Update lines
            self.lclick_pos = event.xdata
            self.plot_update()

    def on_plot_over(self, event):
        if not self.is_plotnav_active():
            return

        # Update lines
        if event.xdata is not None and event.ydata is not None:
            self.lover_pos = event.xdata
        else:
            self.lover_pos = 0

        if self.plotbackground is not None:
            self.plot_update()

    def plot_frame(self, x):
        # Update lines
        self.lframe_pos = x
        self.plot_update()

    def plot_update(self):
        self.figure.canvas.restore_region(self.plotbackground)
        for i, (lclick, lover,
                lframe) in enumerate(zip(self.lclick, self.lover,
                                         self.lframe)):
            lclick.set_xdata([self.lclick_pos])
            lover.set_xdata([self.lover_pos])
            lframe.set_xdata([self.lframe_pos])
            self.subplots[i].draw_artist(lclick)
            self.subplots[i].draw_artist(lover)
            self.subplots[i].draw_artist(lframe)
        self.figure.canvas.blit(self.figure.bbox)

    def sound_play(self):
        if self.playing:
            self.sig_sound_play_at.emit(self.sound_start_at)
        elif self.is_sound_loaded():
            self.sound_thread = SoundThread(self.sound, self.sound_start_at,
                                            self.sound_mutex,
                                            self.sound_pause_cond)
            self.sig_sound_play_at.connect(self.sound_thread.play_at)
            self.sig_sound_pause.connect(self.sound_thread.pause)
            self.sig_sound_stop.connect(self.sound_thread.stop)
            self.sound_thread.sig_frame.connect(self.plot_frame)
            self.sound_thread.finished.connect(self.on_sound_done)
            self.sound_thread.start()

            self.playing = True
            self.update_ui()

    def sound_stop(self):
        self.sig_sound_stop.emit()
        self.sound_mutex.lock()
        self.sound_pause_cond.wakeAll()
        self.sound_mutex.unlock()

    def sound_pause(self):  # Toggle
        if self.sound_paused:
            self.sig_sound_pause.emit()
            self.sound_mutex.lock()
            self.sound_pause_cond.wakeAll()
            self.sound_mutex.unlock()
        else:
            self.sig_sound_pause.emit()
        self.sound_paused = not self.sound_paused
        self.update_ui()

    def on_sound_done(self):
        self.playing = False
        self.sound_paused = False
        self.update_ui()
        self.lframe_pos = 0
        self.plot_update()

    def record(self):  # Toggle
        if self.recording:
            self.sig_record_stop.emit()
        else:
            self.recording = True
            bit_depth = self.bit_depths[self.cb_bit_depth.currentIndex()]
            rate = self.sampling_rates[self.cb_sample_rate.currentIndex()]
            self.record_thread = RecordThread(
                bit_depth, rate, 2)  # Always record in stereo (2 channels)
            self.sig_record_stop.connect(self.record_thread.stop)
            self.record_thread.sig_return.connect(self.on_record_return)
            self.record_thread.start()
            self.update_ui()

    def on_record_return(self, data, sample_width, rate, channels):
        self.load_signal(data, sample_width, rate, channels)
        self.recording = False
        self.update_ui()
コード例 #5
0
class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.ax = None

        self.orig_img = None
        self.img = None

        self.initUI()

    def initUI(self):
        spacer = QSpacerItem(50, 0, QSizePolicy.Minimum)
        spacer_small = QSpacerItem(10, 0, QSizePolicy.Minimum)

        # File selector
        lbl_file = QLabel("File:")
        self.txt_file = QLineEdit()
        self.txt_file.setPlaceholderText("Select file ...")
        btn_file = QPushButton("Select")
        btn_file.clicked.connect(self.show_open_dialog)

        # Save
        self.btn_save = QPushButton("Save")
        self.btn_save.clicked.connect(self.show_save_dialog)

        # Reset
        self.btn_reset = QPushButton("Reset")
        self.btn_reset.setToolTip(
            "Show originally loaded image (reset all modifications)")
        self.btn_reset.clicked.connect(lambda: self.plot_image(self.orig_img))

        # Histogram
        self.btn_hist = QPushButton("Histogram")
        self.btn_hist.setToolTip("Draw histogram of current image")
        self.btn_hist.clicked.connect(self.histogram)

        # Graph space
        self.figure = Figure()
        FigureCanvas(self.figure)
        self.figure.canvas.setMinimumHeight(300)

        # Conversion to Grayscale
        self.cb_gray = QComboBox()
        self.cb_gray.setToolTip("Grayscale conversion method")
        self.cb_gray.addItems(["Average", "Red", "Green", "Blue"])
        self.btn_gray = QPushButton("Grayscale")
        self.btn_gray.setToolTip("Convert loaded image to grayscale image")
        self.btn_gray.clicked.connect(
            lambda: self.grayscale(self.cb_gray.currentIndex() - 1))

        # Segmentation / Binarization
        self.segment_thresh = QLineEdit()
        self.segment_thresh.setText("100")
        self.segment_thresh.setToolTip("Segmentation threshold")
        self.segment_thresh.setMaximumWidth(30)
        self.segment_thresh.setValidator(QIntValidator(0, 255))
        self.btn_segment = QPushButton("Binarize")
        self.btn_segment.setToolTip(
            "Convert loaded image to binary image using segmentation")
        self.btn_segment.clicked.connect(
            lambda: self.binarize(int(self.segment_thresh.text())))

        # Graph toolbar
        self.plotnav = NavigationToolbar(self.figure.canvas,
                                         self.figure.canvas)
        self.plotnav.setStyleSheet("QToolBar { border: 0px }")
        self.plotnav.setOrientation(Qt.Vertical)

        # Image processing implementation
        self.cb_imgproc_impl = QComboBox()
        self.cb_imgproc_impl.setToolTip("Processing implementation")
        self.cb_imgproc_impl.addItems(["OpenCV", "SciPy", "Manual"])

        # Smooth / Blur
        self.smooth_intensity = QLineEdit()
        self.smooth_intensity.setText("5")
        self.smooth_intensity.setToolTip(
            "Smooth intensity (must at least 3 and odd)")
        self.smooth_intensity.setMaximumWidth(30)
        self.smooth_intensity.setValidator(QIntValidator(0, 255))
        self.btn_smooth = QPushButton("Smooth")
        self.btn_smooth.setToolTip("Smooth (blur) current image")
        self.btn_smooth.clicked.connect(
            lambda: self.smooth(int(self.smooth_intensity.text())))

        # Sharpen
        self.sharpen_intensity = QLineEdit()
        self.sharpen_intensity.setText("5")
        self.sharpen_intensity.setToolTip(
            "Sharpen intensity (must be at least 5)")
        self.sharpen_intensity.setMaximumWidth(30)
        self.sharpen_intensity.setValidator(QIntValidator(0, 255))
        self.btn_sharpen = QPushButton("Sharpen")
        self.btn_sharpen.setToolTip("Sharpen current image")
        self.btn_sharpen.clicked.connect(
            lambda: self.sharpen(int(self.sharpen_intensity.text())))

        # Edge detection
        self.edge_intensity = QLineEdit()
        self.edge_intensity.setText("4")
        self.edge_intensity.setToolTip(
            "Edge detection intensity (must be at least 4)")
        self.edge_intensity.setMaximumWidth(30)
        self.edge_intensity.setValidator(QIntValidator(0, 255))
        self.btn_edge = QPushButton("Detect Edges")
        self.btn_edge.setToolTip("Detect edges on current image")
        self.btn_edge.clicked.connect(
            lambda: self.detect_edges(int(self.edge_intensity.text())))

        # Dilate
        self.dilate_intensity = QLineEdit()
        self.dilate_intensity.setText("5")
        self.dilate_intensity.setToolTip(
            "Dilation intensity (must be at least 2)")
        self.dilate_intensity.setMaximumWidth(30)
        self.dilate_intensity.setValidator(QIntValidator(0, 255))
        self.btn_dilate = QPushButton("Dilate")
        self.btn_dilate.setToolTip("Dilate current image")
        self.btn_dilate.clicked.connect(
            lambda: self.dilate(int(self.dilate_intensity.text())))

        # Erode
        self.erode_intensity = QLineEdit()
        self.erode_intensity.setText("5")
        self.erode_intensity.setToolTip(
            "Erosion intensity (must be at least 2)")
        self.erode_intensity.setMaximumWidth(30)
        self.erode_intensity.setValidator(QIntValidator(0, 255))
        self.btn_erode = QPushButton("Erode")
        self.btn_erode.setToolTip("Erode current image")
        self.btn_erode.clicked.connect(
            lambda: self.erode(int(self.erode_intensity.text())))

        # Layout
        hbox_top = QHBoxLayout()
        hbox_top.addWidget(lbl_file)
        hbox_top.addWidget(self.txt_file)
        hbox_top.addWidget(btn_file)
        hbox_top.addWidget(self.btn_save)
        hbox_top.addWidget(self.btn_reset)
        hbox_top.addStretch()
        hbox_top.addSpacerItem(spacer)
        hbox_top.addWidget(self.btn_hist)
        hbox_top.addStretch()
        hbox_top.addSpacerItem(spacer)
        hbox_top.addWidget(self.cb_gray)
        hbox_top.addWidget(self.btn_gray)
        hbox_top.addSpacerItem(spacer_small)
        hbox_top.addWidget(self.segment_thresh)
        hbox_top.addWidget(self.btn_segment)

        hbox_bot = QHBoxLayout()
        hbox_bot.addWidget(self.cb_imgproc_impl)
        hbox_bot.addStretch()
        hbox_bot.addSpacerItem(spacer)
        hbox_bot.addWidget(self.smooth_intensity)
        hbox_bot.addWidget(self.btn_smooth)
        hbox_bot.addWidget(self.sharpen_intensity)
        hbox_bot.addWidget(self.btn_sharpen)
        hbox_bot.addWidget(self.edge_intensity)
        hbox_bot.addWidget(self.btn_edge)
        hbox_bot.addStretch()
        hbox_bot.addSpacerItem(spacer)
        hbox_bot.addWidget(self.dilate_intensity)
        hbox_bot.addWidget(self.btn_dilate)
        hbox_bot.addWidget(self.erode_intensity)
        hbox_bot.addWidget(self.btn_erode)

        vbox = QVBoxLayout()
        vbox.addLayout(hbox_top)
        vbox.addWidget(self.figure.canvas)
        vbox.addLayout(hbox_bot)

        self.update_ui()

        # Window
        self.setLayout(vbox)
        self.setGeometry(300, 300, 1000, 500)
        self.setWindowTitle("Signal Processor - Image")
        self.show()

    # Overriden resize event
    def resizeEvent(self, resizeEvent):
        self.plotnav.move(self.width() - 55, 0)

    def update_ui(self):
        block_general = not self.is_image_loaded()

        self.btn_save.setDisabled(block_general)
        self.btn_reset.setDisabled(block_general)
        self.btn_hist.setDisabled(block_general)
        self.btn_gray.setDisabled(block_general)
        self.btn_segment.setDisabled(block_general)
        self.btn_smooth.setDisabled(block_general)
        self.btn_sharpen.setDisabled(block_general)
        self.btn_dilate.setDisabled(block_general)
        self.btn_erode.setDisabled(block_general)
        self.btn_edge.setDisabled(block_general)

    def show_open_dialog(self):
        fname, ext = QFileDialog.getOpenFileName(
            self, "Open file", filter="Image (*.png *.jpg *.bmp)")
        if fname and self.load_image(fname):
            self.txt_file.setText(fname)

    def show_save_dialog(self):
        fname, ext = QFileDialog.getSaveFileName(
            self, "Save file", filter="Image (*.png *.jpg *.bmp)")
        if fname and self.is_image_loaded():
            # Save as PNG if not set
            if '.' not in fname:
                fname += ".png"

            cv2.imwrite(fname, cv2.cvtColor(self.img, cv2.COLOR_RGB2BGR))
            self.txt_file.setText(fname)

    def load_image(self, file):
        if not os.path.isfile(file):
            return False

        # Read image and convert from BGR (OpenCV default) to RGB
        self.orig_img = cv2.imread(file)
        self.orig_img = cv2.cvtColor(self.orig_img, cv2.COLOR_BGR2RGB)
        self.img = self.orig_img

        self.plot_image(self.orig_img)
        self.update_ui()
        return True

    def is_image_loaded(self):
        return self.img is not None

    def reset_plot(self):
        self.figure.clear()
        self.ax = self.figure.add_subplot(1, 1, 1)

    def plot_image(self, img):
        self.reset_plot()
        self.ax.axis("off")

        self.ax.imshow(img, cmap='gray' if len(img.shape) < 3 else None)
        self.figure.canvas.draw()

        self.img = img

    # Draw histogram of current image
    def histogram(self):
        self.reset_plot()
        self.ax.margins(0)

        # Plot each channel on RGB image or only first channel on grayscale image
        colors = ('r', 'g', 'b') if len(self.img.shape) > 2 else ('b', )
        for i, color in enumerate(colors):
            hist = cv2.calcHist([self.img], [i], None, [256], [0, 256])
            self.ax.plot(hist, color=color)

        self.figure.canvas.draw()

    # Convert current image to grayscale
    def grayscale(self, type=-1):  # -1 - Average, 0 - Red, 1 - Green, 2 - Blue
        # Do nothing if already grayscale
        if len(self.img.shape) < 3:
            return self.img

        if type < 0:
            # Convert to grayscale by averaging all channels
            img_gray = cv2.cvtColor(self.img, cv2.COLOR_RGB2GRAY)
        else:
            # Convert to grayscale by taking one channel
            img_gray = self.img[:, :, type]

        self.plot_image(img_gray)

    # Binarize current image
    def binarize(self, threshold=0):
        # Make sure we are operating on grayscale image (applied to original image)
        self.grayscale()
        _, img_bin = cv2.threshold(self.img, threshold, 255,
                                   cv2.THRESH_BINARY_INV)

        self.plot_image(img_bin)

    # Get convolution implementation from combo box (lower-case text)
    def get_imgproc_impl(self):
        return self.cb_imgproc_impl.currentText().lower()

        # Smooth (blur) current image
    def smooth(self, intensity=5):
        if intensity < 3 or intensity % 2 == 0:
            print(
                "Error! Smooth intensity should be at least 3 and an odd integer!"
            )

        kernel = np.ones((intensity, intensity)) / intensity**2
        img_smooth = self.convolve2d(kernel)

        self.plot_image(img_smooth)

    # Sharpen current image
    def sharpen(self, intensity=5):
        if intensity < 5:
            print(
                "Warning! Sharpen intensity should be at least 5! Defaulting to 5!"
            )

        kernel = np.array(([0, -1, 0], [-1, max(intensity, 5), -1], [0, -1,
                                                                     0]))
        img_sharp = self.convolve2d(kernel)

        self.plot_image(img_sharp)

    # Detect edges on current image
    def detect_edges(self, intensity=5):
        if intensity < 4:
            print(
                "Warning! Edge detection intensity should be at least 4! Defaulting to 4!"
            )

        kernel = np.array(([0, 1, 0], [1, -max(intensity, 4), 1], [0, 1, 0]))
        img_edges = self.convolve2d(kernel)

        self.plot_image(img_edges)

    # Dilate current image
    def dilate(self, intensity=5):
        if intensity < 2:
            print(
                "Warning! Dilation intensity should be at least 2! Defaulting to 2!"
            )
            intensity = 2

        kernel = np.full((intensity, intensity), 255)

        imgproc = self.get_imgproc_impl()
        if imgproc == "opencv":
            # OpenCV dilate
            img_dilate = cv2.dilate(self.img, kernel)
        elif imgproc == "scipy":
            # SciPy grey_dilation
            img_dilate = self.morph2d_scipy(
                self.img, kernel, morph_func=morphology.grey_dilation)
        elif imgproc == "manual":
            # Manual morphology
            img_dilate = self.convolve2d_manual(
                self.img,
                kernel,
                func=lambda roi, kernel: np.max(roi[kernel.astype(np.bool)]))
        else:
            print("Error! Unknown image processing implementation!")
            img_dilate = self.img

        self.plot_image(img_dilate)

    # Erode current image
    def erode(self, intensity=5):
        if intensity < 2:
            print(
                "Warning! Erosion intensity should be at least 2! Defaulting to 2!"
            )
            intensity = 2

        kernel = np.full((intensity, intensity), 255, dtype=np.uint8)

        imgproc = self.get_imgproc_impl()
        if imgproc == "opencv":
            img_erode = cv2.erode(self.img, kernel)
        elif imgproc == "scipy":
            img_erode = self.morph2d_scipy(self.img,
                                           kernel,
                                           morph_func=morphology.grey_erosion)
        elif imgproc == "manual":
            img_erode = self.convolve2d_manual(
                self.img,
                kernel,
                func=lambda roi, kernel: np.min(roi[kernel.astype(np.bool)]))
        else:
            print("Error! Unknown image processing implementation!")
            img_erode = self.img

        self.plot_image(img_erode)

    # Convolve given image
    def convolve2d(self, kernel):
        imgproc = self.get_imgproc_impl()
        if imgproc == "opencv":
            return cv2.filter2D(self.img, -1, kernel)
        elif imgproc == "scipy":
            return self.convolve2d_scipy(self.img, kernel)
        elif imgproc == "manual":
            return self.convolve2d_manual(self.img,
                                          kernel,
                                          func=lambda roi, kernel:
                                          (roi * kernel).sum())

        print("Error! Unknown image processing implementation!")
        return self.img

    # Convolve given image with SciPy
    def convolve2d_scipy(self, img, kernel):
        if len(img.shape) < 3:
            # Grayscale
            return signal.convolve2d(img, kernel, mode="same", boundary="symm")
        else:
            # Color - convolve each channel
            img_conv = []
            for ch in range(img.shape[2]):
                img_conv_ch = signal.convolve2d(img[:, :, ch],
                                                kernel,
                                                mode="same",
                                                boundary="symm")
                img_conv.append(img_conv_ch)

            # Stack channels, clip them to [0, 255] and represent as original image (prevent invalid range)
            return np.clip(np.stack(img_conv, axis=2), 0,
                           255).astype(img.dtype)

    # Convolve given image with manual implementation and given pixel functor
    def convolve2d_manual(self, img, kernel, func=None):
        if func is None:
            print("Error! Invalid convolution functor!")
            return img

        # Get spatial dimensions of the image and kernel
        (img_h, img_w) = img.shape[:2]
        (kern_h, kern_w) = kernel.shape[:2]

        # Pad border
        pad = int((kern_w - 1) / 2)
        img = cv2.copyMakeBorder(img, pad, pad, pad, pad, cv2.BORDER_REPLICATE)

        if len(img.shape) < 3:
            # Grayscale
            return self.convolve2d_manual_channel(img,
                                                  kernel, (img_h, img_w),
                                                  pad,
                                                  func=func)
        else:
            # Color - convolve each channel
            img_conv = []
            for ch in range(img.shape[2]):
                img_conv_ch = self.convolve2d_manual_channel(img[:, :, ch],
                                                             kernel,
                                                             (img_h, img_w),
                                                             pad,
                                                             func=func)
                img_conv.append(img_conv_ch)

            # Stack channels, clip them to [0, 255] and represent as original image (prevent invalid range)
            return np.clip(np.stack(img_conv, axis=2), 0,
                           255).astype(img.dtype)

    # Convolve one channel of given image with manual implementation
    def convolve2d_manual_channel(self, img, kernel, img_size, pad, func):
        (img_h, img_w) = img_size

        # Slide the kernel over the image from left to right and top to bottom
        img_conv = np.zeros((img_h, img_w))
        for y in np.arange(pad, img_h + pad):
            for x in np.arange(pad, img_w + pad):
                # Extract region of interest (ROI) of the image by extracting the center region
                roi = img[y - pad:y + pad + 1, x - pad:x + pad + 1]
                # Perform convolution (element-wise multiplication between ROI and kernel and sum of matrix)
                k = func(roi, kernel)
                # Store convolved value in the current coordinate
                img_conv[y - pad, x - pad] = k

        # Rescale convolved image to be in range [0, 255]
        return rescale_intensity(img_conv, in_range=(0, 255)) * 255

    # Morph current image with SciPy
    def morph2d_scipy(self, img, kernel, morph_func=None):
        if morph_func is None:
            print("Error! Invalid morphology functor!")
            return img

        # SciPy does not like non-zero kernels
        kernel = np.zeros(kernel.shape)

        if len(img.shape) < 3:
            # Grayscale
            return morph_func(img, structure=kernel)
        else:
            # Color - erode each channel
            img_morph = []
            for ch in range(img.shape[2]):
                img_morph_ch = morph_func(img[:, :, ch],
                                          structure=kernel).astype(img.dtype)
                img_morph.append(img_morph_ch)

            # Stack channels, clip them to [0, 255] and represent as original image (prevent invalid range)
            return np.clip(np.stack(img_morph, axis=2), 0,
                           255).astype(img.dtype)
コード例 #6
0
    def __init__(self, parent=None):
        super(ApplicationWindow, self).__init__(parent)
        
        QMainWindow.__init__(self)
        self.setAttribute(Qt.WA_DeleteOnClose)

        self.setWindowTitle("xray yield simulation")
             
        self.resize(1200, 750)
        
        self.er_low=0.1# keV
        self.er_high=20.# keV
        self.er_step=0.001# keV
        self.enes_keV=np.arange(self.er_low,self.er_high+self.er_low+self.er_step,self.er_step)
        self.qeout=np.zeros_like(self.enes_keV)# for output
        self.flout=np.zeros_like(self.enes_keV)# for output
        # IUPAC macro
        self.LINES=['KL3','KL2','KM3','KM2',
                    'L3M5','L3M4','L2M4','L3N5',
                    'L1M3','L1M2','L2N4','L3M1']
        # Siegbahn
        self.SGBLINES=['KA1','KA2','KB1','KB3',
                       'LA1','LA2','LB1','LB2',
                       'LB3','LB4','LG1','LL']

        # radionuclide list
        self.RDNLIST=list(xrl.GetRadioNuclideDataList())
        #['55Fe','57Co','109Cd','125I','137Cs',
        #'133Ba','153Gd','238Pu','241Am','244Cm']
        # half life
        self.RDNHL=[1006.70,272.11,463.26,59.49,11018.3,
                    3854.7,239.472,32031.74,157857.678,6610.52]# days

        # universal parameters
        self.rad_duration=3600.# sec
        self.beam_duration=7200.# sec
        self.detector_resolution=8.0# eV
        self.detector_solidangle=1.0
        
        self.csvd="./csv/"
        IUPACdf=pd.read_csv(self.csvd+"IUPAC_macro.csv")
        self.IUPACmac=IUPACdf['IUPAC_macro'].values
        self.allLINES=[xrl.__getattribute__(mac) for mac in self.IUPACmac] 
        
        # ---- layout ----
        # main widget
        main_widget = QWidget(self)
        self.setCentralWidget(main_widget)
        main_layout = QHBoxLayout()

          
        # target, detector, filter materials
        topleft=QWidget()
        topleftbox=QVBoxLayout()


        self.fopenButton = QPushButton("Load csv file")
        self.fopenButton.clicked.connect(self.openFileNameDialog)
        
        self.default_none          = "None (reset all)"
        self.default_tes_jparc_mlf = "TES(Bi) J-PARC MLF"
        self.default_tes_spring8   = "TES(Bi) Spring-8"
        self.default_tes_sn        = "TES(Sn)"
        self.default_cdte          = "CdTe"
        self.default_ge            = "Ge"
        self.default_si            = "Si"
        self.chkbox0 = QCheckBox(self.default_none)
        self.chkbox1 = QCheckBox(self.default_tes_jparc_mlf)
        self.chkbox2 = QCheckBox(self.default_tes_spring8)
        self.chkbox3 = QCheckBox(self.default_tes_sn)
        self.chkbox4 = QCheckBox(self.default_cdte)
        self.chkbox5 = QCheckBox(self.default_ge)
        self.chkbox6 = QCheckBox(self.default_si)
        self.chkbg = QButtonGroup()
        self.chkbg.addButton(self.chkbox0,1)
        self.chkbg.addButton(self.chkbox1,2)
        self.chkbg.addButton(self.chkbox2,3)
        self.chkbg.addButton(self.chkbox3,4)
        self.chkbg.addButton(self.chkbox4,5)
        self.chkbg.addButton(self.chkbox5,6)
        self.chkbg.addButton(self.chkbox6,7)
        self.chkbg.buttonClicked[QAbstractButton].connect(self.btngroup)
        self.chkbox0.setChecked(True)
        ltboxw=QWidget()
        ltbox=QVBoxLayout(ltboxw)
        ltbox.setContentsMargins(0, 0, 0, 0)
        ltbox.addWidget(QLabel("Default setting"))
        ltbox.addWidget(self.fopenButton)
        ltbox.addWidget(self.chkbox0)
        ltbox.addWidget(self.chkbox1)
        ltbox.addWidget(self.chkbox2)
        ltboxw_02=QWidget()
        ltbox_02=QHBoxLayout(ltboxw_02)
        ltbox_02.setContentsMargins(0, 0, 0, 0)
        ltbox_02.addWidget(self.chkbox3)
        ltbox_02.addWidget(self.chkbox4)
        ltbox_02.addWidget(self.chkbox5)
        ltbox_02.addWidget(self.chkbox6)
                
        ltboxw2=QWidget()
        ltbox2=QVBoxLayout(ltboxw2)
        qtab=QTabWidget()
        self.bem={}
        self.tgt={}
        self.det={}
        self.bet={}
        self.rad={}
        self.dets=[]
        self.bets=[]
        self.rads=[]
        self.bemtab=material.MaterialTabWidget(parent=self,name='bem')
        self.tgttab=material.MaterialTabWidget(parent=self,name='tgt')
        self.dettab=material.MaterialTabWidget(parent=self,name='det')
        self.bettab=material.MaterialTabWidget(parent=self,name='bet')
        self.radtab=material.MaterialTabWidget(parent=self,name='rad')
        qtab.addTab(self.dettab,'Detector')
        qtab.addTab(self.tgttab,'Target')
        qtab.addTab(self.bettab,'Filter')
        qtab.addTab(self.radtab,'RadioNucl')
        qtab.addTab(self.bemtab,'Beam')
        ltbox2.addWidget(qtab)
        
        topleftbox.addWidget(ltboxw)
        topleftbox.addWidget(ltboxw_02)
        topleftbox.addWidget(ltboxw2)
        topleft.setLayout(topleftbox)


        plotButton = QPushButton("Plot")
        plotButton.clicked.connect(self._plot_trans_fluor)
        saveButton = QPushButton("Save")
        saveButton.clicked.connect(self._save_trans_fluor)
        #plotButton = QPushButton("Plot transmission")
        #plotButton.clicked.connect(self._update_trans_cv)
        #fluorButton = QPushButton("Plot flurorescence")
        #fluorButton.clicked.connect(self._update_fluor_cv)
        
        # energy range
        rangebox=QHBoxLayout()
        self.ene_range_low_le = QLineEdit()
        self.ene_range_low_le.setValidator(QDoubleValidator(0.,800.,999))
        self.ene_range_low_le.returnPressed.connect(self.apply_enerangelow)
        self.ene_range_low_le.setText("0.1")
        self.ene_range_high_le = QLineEdit()
        self.ene_range_high_le.setValidator(QDoubleValidator(0.,800.,999))
        self.ene_range_high_le.returnPressed.connect(self.apply_enerangehigh)
        self.ene_range_high_le.setText("20.")
        self.ene_range_step_le = QLineEdit()
        self.ene_range_step_le.setValidator(QDoubleValidator(0.,800.,999))
        self.ene_range_step_le.returnPressed.connect(self.apply_enerangestep)
        self.ene_range_step_le.setText("0.001")
        rangebox.addWidget(QLabel("Low:"))
        rangebox.addWidget(self.ene_range_low_le)
        rangebox.addWidget(QLabel("High:"))
        rangebox.addWidget(self.ene_range_high_le)
        rangebox.addWidget(QLabel("Step:"))
        rangebox.addWidget(self.ene_range_step_le)

        # detector resolution
        self.detector_resolution_le = QLineEdit()
        self.detector_resolution_le.setValidator(QDoubleValidator(0.,1e5,999))
        self.detector_resolution_le.returnPressed.connect(self.apply_detector_resolution)
        self.detector_resolution_le.setText(str(self.detector_resolution))
        # detector solidangle
        self.detector_solidangle_le = QLineEdit()
        self.detector_solidangle_le.setValidator(QDoubleValidator(0.,1.,999))
        self.detector_solidangle_le.returnPressed.connect(self.apply_detector_solidangle)
        self.detector_solidangle_le.setText(str(self.detector_solidangle))
        
        
        # check current setup
        topmiddle=QWidget()
        tmbox=QVBoxLayout()
        self.cc_table = QTableWidget()
        self.init_cc_table()
        self.not_draw_lines=[]
        self.line_table = QTableWidget()
        self.init_line_table()

        tmbox.addWidget(QLabel("Plot"))
        tmbox.addWidget(QLabel("Energy range (keV)"))
        tmbox.addLayout(rangebox)
        tmbox.addWidget(plotButton)
        tmbox.addWidget(saveButton)
        self.chkbox_resol = QCheckBox("Detector resolution FWHM (eV)")
        self.chkbox_resol.stateChanged.connect(self.chkbox_resol_action)
        self.chkbox_resol.setChecked(True)
        #tmbox.addWidget(QLabel("Detector resolution FWHM (eV)"))
        tmbox.addWidget(self.chkbox_resol)
        tmbox.addWidget(self.detector_resolution_le)
        tmbox.addWidget(QLabel("Solidangle ratio (0.-1.)"))
        tmbox.addWidget(self.detector_solidangle_le)
        
        #tmbox.addWidget(fluorButton)
        tmbox.addWidget(QLabel("Current setting"))
        tmbox.addWidget(self.cc_table)
        tmbox.addWidget(QLabel("X-ray lines"))
        tmbox.addWidget(self.line_table)
        topmiddle.setLayout(tmbox)
       
        topright=QWidget()
        trbox=QVBoxLayout()
        mini_width=300
        self.fig_tr = Figure(figsize=(8, 6), dpi=80)
        trans_cv = FigureCanvas(self.fig_tr)
        trans_tlbar = NavigationToolbar(trans_cv, self)
        trans_tlbar.setMinimumWidth(mini_width)
        trans_tlbar.setStyleSheet("QToolBar { border: 0px }")
        self.ax = trans_cv.figure.subplots()
        self.fig_fl = Figure(figsize=(8, 6), dpi=80)
        fluor_cv = FigureCanvas(self.fig_fl)
        fluor_tlbar = NavigationToolbar(fluor_cv, self)
        fluor_tlbar.setMinimumWidth(mini_width)
        fluor_tlbar.setStyleSheet("QToolBar { border: 0px }")
        self.ax_fl = fluor_cv.figure.subplots()
        trbox.addWidget(trans_tlbar)
        trbox.addWidget(trans_cv)
        trbox.addWidget(fluor_cv)
        trbox.addWidget(fluor_tlbar)
        topright.setLayout(trbox)
        topright.layout().setSpacing(0)

        ## cosole outputs
        ## used for debug
        #resultTE = QTextEdit()
        #resultTE.setReadOnly( True )
        #resultTE.setUndoRedoEnabled( False )
        #sys.stdout = line_wrap.Logger(resultTE, sys.stdout, QColor(0, 100, 100))
        #sys.stderr = line_wrap.Logger(resultTE, sys.stderr, QColor(200, 0, 0))
        
        splitter1 = QSplitter(Qt.Horizontal)
        splitter1.addWidget(topleft)
        splitter1.addWidget(topmiddle)
        splitter3 = QSplitter(Qt.Horizontal)
        splitter3.addWidget(splitter1)
        splitter3.addWidget(topright)
        #splitter2 = QSplitter(Qt.Vertical)
        #splitter2.addWidget(splitter3)
        #splitter2.addWidget(resultTE)
        #main_layout.addWidget(splitter2)
        main_layout.addWidget(splitter3)
        main_widget.setLayout(main_layout)
        main_widget.setFocus()
        
        self.show()