import math
import warnings
import urllib.request
from urllib.error import HTTPError
import os

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QApplication, QDialog, QGroupBox, QGridLayout, QLabel,
                             QVBoxLayout, QWidget, QHBoxLayout,
                             QDoubleSpinBox, QPushButton, QTabWidget, QCheckBox,
                             QProgressBar, QComboBox, QSizePolicy)
from PyQt5.QtGui import QPixmap
import numpy as np
from astropy import units as u
from astropy.visualization import quantity_support

import lifesim as ls
from lifesim.util.radiation import black_body
from lifesim.gui.custom_widgets import (DoubleBoxLabel, BoxLabel, StringBoxLabel, DoubleBoxRange,
                                        FileBrowser, FileSaver, PlotCanvas)

# change the directory to the path of the spectrum_gui.py file to ensure that the logo is imported
# correctly
os.chdir(os.path.dirname(__file__))

quantity_support()

try:
    urllib.request.urlretrieve("https://github.com/fdannert/LIFEsim/raw/master/"
                               "lifesim/gui/logo_blue.png", "logo_blue.png")
except HTTPError:
    pass


class Frame(QDialog):
    def __init__(self, parent=None):
        super(Frame, self).__init__(parent)

        self.bus = ls.Bus()
        self.bus.data.options.set_scenario('baseline')

        self.spec_import = ls.SpectrumImporter()

        inst = ls.Instrument(name='life')
        self.bus.add_module(module=inst)

        transm = ls.TransmissionMap(name='transm')
        self.bus.add_module(module=transm)

        sl = ls.PhotonNoiseStar(name='sl')
        self.bus.add_module(module=sl)

        lz = ls.PhotonNoiseLocalzodi(name='lz')
        self.bus.add_module(module=lz)

        ez = ls.PhotonNoiseExozodi(name='ez')
        self.bus.add_module(module=ez)

        self.bus.connect(('life', 'transm'))
        self.bus.connect(('sl', 'transm'))

        # LIFEsim simulator setup

        # ----- GUI Setup ------

        # create all sub-widgets (sorted after functionality)
        self.create_instrument()
        self.create_star()
        self.create_planet()
        self.preview_spectrum()
        self.create_simulation()
        self.create_scenario()
        self.create_logo()
        self.view_spectrum()
        self.create_input()

        # structure all setting into one widget
        planet_inst = QWidget()

        ps_layout = QVBoxLayout(planet_inst)
        ps_layout.addWidget(self.instrument)
        ps_layout.addWidget(self.planet)

        settings = QWidget()

        s_layout = QHBoxLayout(settings)
        s_layout.addWidget(self.star)
        s_layout.addWidget(planet_inst)

        # Inputs
        preview = QWidget()

        p_layout = QHBoxLayout(preview)
        p_layout.addWidget(self.input)
        p_layout.addWidget(self.s_preview)

        # create the tab widget
        tabs = QTabWidget()
        tabs.addTab(settings, 'Settings')
        tabs.addTab(preview, 'Preview')
        tabs.addTab(self.s_result, 'Results')

        #structure the sidebar in one widget
        sidebar = QWidget()
        sb_layout = QVBoxLayout(sidebar)
        sb_layout.addWidget(self.simulation, alignment=Qt.AlignTop)
        sb_layout.addWidget(self.scenario, alignment=Qt.AlignTop)
        sb_layout.addWidget(self.logo, alignment=Qt.AlignBottom)

        # layout the main body of the frame
        body_layout = QGridLayout()
        body_layout.addWidget(tabs, 0, 1)
        body_layout.addWidget(sidebar, 0, 0)
        self.setLayout(body_layout)

        self.setWindowTitle('LIFEsim lite: Spectrum Simulator')

        self.r_spec = None

    def create_instrument(self):
        self.instrument = QGroupBox('Instrument')

        self.diameter = DoubleBoxLabel(label='Aperture Diameter',
                                       maxi=10.,
                                       mini=0.25,
                                       step=0.5,
                                       value=2,
                                       suffix='m')

        self.wl_range = DoubleBoxRange(label='Wavelength Range')

        self.spec_res = BoxLabel(label='Spectral Resolution',
                                 mini=1,
                                 maxi=100,
                                 value=20,
                                 suffix='')

        layout = QGridLayout(self.instrument)
        layout.addWidget(self.diameter.label, 0, 0)
        layout.addWidget(self.diameter, 0, 1)
        layout.addWidget(self.spec_res. label, 1, 0)
        layout.addWidget(self.spec_res, 1, 1)
        layout.addWidget(self.wl_range.label, 2, 0)
        layout.addWidget(self.wl_range, 2, 1)

    def create_star(self):
        self.star = QGroupBox('Star')
        temp = QWidget()

        self.temp_s = DoubleBoxLabel(label='Temperature',
                                     mini=0.,
                                     maxi=100000.,
                                     step=100.,
                                     value=5778.,
                                     suffix='K')
        self.temp_s.label.setToolTip('Test Tip')

        self.radius_s = DoubleBoxLabel(label='Radius',
                                       mini=0.,
                                       maxi=2158.,
                                       step=0.5,
                                       value=1.,
                                       suffix='R☉')

        self.distance_s = DoubleBoxLabel(label='Distance',
                                         mini=0.,
                                         maxi=50.,
                                         step=1.,
                                         value=10.,
                                         suffix='pc')

        self.lat = DoubleBoxLabel(label='Ecliptic Latitude',
                                  mini=0.,
                                  maxi=2*math.pi,
                                  step=0.1,
                                  value=0.79,
                                  suffix='rad')

        self.z = DoubleBoxLabel(label='Exozodis',
                                mini=0.,
                                maxi=20.,
                                step=0.5,
                                value=1.,
                                suffix='z')

        layout = QGridLayout(temp)
        layout.addWidget(self.temp_s.label, 0, 0)
        layout.addWidget(self.temp_s, 0, 1)

        layout.addWidget(self.radius_s.label, 1, 0)
        layout.addWidget(self.radius_s, 1, 1)

        layout.addWidget(self.distance_s.label, 2, 0)
        layout.addWidget(self.distance_s, 2, 1)

        layout.addWidget(self.lat.label, 3, 0)
        layout.addWidget(self.lat, 3, 1)

        layout.addWidget(self.z.label, 4, 0)
        layout.addWidget(self.z, 4, 1)

        s_layout = QVBoxLayout(self.star)
        s_layout.addWidget(temp)

    def create_planet(self):
        self.planet = QGroupBox('Planet')

        self.angsep = DoubleBoxLabel(label='Angular Separation',
                                     mini=0.,
                                     maxi=1.,
                                     value=0.1,
                                     step=0.1,
                                     suffix='arcsec')

        self.radius_p = DoubleBoxLabel(label='Radius',
                                       mini=0.,
                                       maxi=10.,
                                       value=1.,
                                       step=0.5,
                                       suffix='R⊕')

        layout = QGridLayout(self.planet)
        layout.addWidget(self.angsep.label, 0, 0)
        layout.addWidget(self.angsep, 0, 1)
        layout.addWidget(self.radius_p.label, 1, 0)
        layout.addWidget(self.radius_p, 1, 1)

    def preview_spectrum(self):
        self.s_preview = QWidget()
        self.p_plot = PlotCanvas(self, width=5, height=4, dpi=100)

        top = QWidget()
        self.prev_kind = QComboBox()
        self.prev_kind.addItems(['given units', 'converted units'])

        button = QPushButton('Preview Spectrum')
        button.clicked.connect(self.show_preview)

        layout_t = QHBoxLayout(top)
        layout_t.addWidget(self.prev_kind)
        layout_t.addWidget(button)

        self.error_field = QLabel()
        self.error_field.setStyleSheet("QLabel { color : red; }")

        self.p_plot.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        layout = QVBoxLayout(self.s_preview)
        layout.addWidget(top, alignment=Qt.AlignTop)
        layout.addWidget(self.p_plot)
        layout.addWidget(self.error_field, alignment=Qt.AlignBottom)

    def view_spectrum(self):
        self.s_result = QWidget()

        self.r_plot = PlotCanvas(self, width=5, height=4, dpi=100)

        self.save_dialog = QGroupBox('Save Spectrum to File')

        self.save = FileSaver()
        button = QPushButton('Save')
        button.clicked.connect(self.save_spectrum)

        layout = QHBoxLayout(self.save_dialog)
        layout.addWidget(self.save)
        layout.addWidget(button)
        self.r_plot.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        layout = QVBoxLayout()
        layout.addWidget(self.r_plot)
        layout.addWidget(self.save_dialog, alignment=Qt.AlignBottom)
        self.s_result.setLayout(layout)

    def create_simulation(self):
        self.simulation = QGroupBox('Simulation')

        time_l = QLabel('Integration Time')

        self.time_b = QDoubleSpinBox()
        self.time_b.setMinimum(0.)
        self.time_b.setMaximum(17520.)
        self.time_b.setSuffix('h')
        self.time_b.setSingleStep(1.)
        self.time_b.setValue(10.)

        t = QWidget()
        t_layout = QVBoxLayout(t)
        t_layout.addWidget(time_l)
        t_layout.addWidget(self.time_b)

        noise_l = QLabel('Noise Sources')

        self.box_sl = QCheckBox('Stellar Leakage')
        self.box_lz = QCheckBox('Localzodi')
        self.box_ez = QCheckBox('Exozodi')
        self.box_sl.setChecked(True)
        self.box_lz.setChecked(True)
        self.box_ez.setChecked(True)

        noise = QWidget()
        n_layout = QVBoxLayout(noise)
        n_layout.addWidget(noise_l)
        n_layout.addWidget(self.box_sl)
        n_layout.addWidget(self.box_lz)
        n_layout.addWidget(self.box_ez)

        simulate = QPushButton('Run Simulation')

        simulate.clicked.connect(self.run_simulation)

        layout = QVBoxLayout(self.simulation)
        layout.addWidget(t, alignment=Qt.AlignBottom)
        layout.addWidget(noise, alignment=Qt.AlignBottom)
        layout.addWidget(simulate, alignment=Qt.AlignBottom)

    def create_scenario(self):
        self.scenario = QGroupBox('Set Scenario')

        optimisic = QPushButton('Optimistic')
        optimisic.clicked.connect(self.set_scenario_opt)
        baseline = QPushButton('Baseline')
        baseline.clicked.connect(self.set_scenario_bas)
        pessimistic = QPushButton('Pessimistic')
        pessimistic.clicked.connect(self.set_scenario_pes)

        layout = QVBoxLayout(self.scenario)
        layout.addWidget(optimisic, alignment=Qt.AlignBottom)
        layout.addWidget(baseline, alignment=Qt.AlignBottom)
        layout.addWidget(pessimistic, alignment=Qt.AlignBottom)

    def create_progress(self):
        self.pb = QGroupBox('Progress')

        self.progress = QProgressBar()
        self.progress.setRange(0, 100)
        self.progress.setValue(50.)
        self.progress.setOrientation(Qt.Vertical)

        layout = QVBoxLayout(self.pb)
        layout.addWidget(self.progress, alignment=Qt.AlignHCenter)

    def create_logo(self):
        self.logo = QWidget()

        image = QLabel()
        pixmap = QPixmap('logo_blue.png').scaledToWidth(150)
        image.setPixmap(pixmap)

        website = QLabel('life-space-mission.com')

        layout = QVBoxLayout(self.logo)
        layout.addWidget(image)
        layout.addWidget(website)

    def create_input(self):
        self.input = QWidget()

        load = QGroupBox('Import')

        self.spec_kind = QComboBox()
        self.spec_kind.addItems(['absolute', 'additive', 'contrast'])

        self.browse = FileBrowser(label='Spectrum')

        self.x_units = StringBoxLabel('x-axis units')
        self.y_units = StringBoxLabel('y-axis units')
        self.temp_p = DoubleBoxLabel(label='Temperature Planet',
                                     mini=0.,
                                     maxi=10000.,
                                     step=1.,
                                     value=0.,
                                     suffix='K')
        temp = QWidget()
        ly = QHBoxLayout(temp)
        ly.addWidget(self.temp_p.label)
        ly.addWidget(self.temp_p)

        layout_l = QVBoxLayout(load)
        layout_l.addWidget(self.spec_kind)
        layout_l.addWidget(self.browse)
        layout_l.addWidget(self.x_units)
        layout_l.addWidget(self.y_units)
        layout_l.addWidget(temp)

        self.temp_p.hide()
        self.spec_kind.currentTextChanged.connect(self.change_visibility)

        param = QGroupBox('Spectrum Parameters')

        self.distance_spec = DoubleBoxLabel(label='Distance',
                                            mini=0.,
                                            maxi=50.,
                                            step=1.,
                                            value=0.,
                                            suffix='pc')

        self.radius_spec = DoubleBoxLabel(label='Radius Planet',
                                          mini=0.,
                                          maxi=10.,
                                          value=0.,
                                          step=0.5,
                                          suffix='R⊕')

        self.time_spec = DoubleBoxLabel(label='Integration Time',
                                        mini=0.,
                                        maxi=60.*60.*24.*365.*10.,
                                        step=60.,
                                        value=0.,
                                        suffix='s')

        layout_p = QGridLayout(param)
        layout_p.addWidget(self.distance_spec.label, 0, 0)
        layout_p.addWidget(self.distance_spec, 0, 1)
        layout_p.addWidget(self.radius_spec.label, 1, 0)
        layout_p.addWidget(self.radius_spec, 1, 1)
        layout_p.addWidget(self.time_spec.label, 2, 0)
        layout_p.addWidget(self.time_spec, 2, 1)

        layout = QVBoxLayout(self.input)
        layout.addWidget(load)
        layout.addWidget(param)

    def update_options(self):
        self.bus.data.options.set_manual(diameter=self.diameter.value(),
                                         wl_min=self.wl_range.lower.value(),
                                         wl_max=self.wl_range.upper.value(),
                                         spec_res=self.spec_res.value())
        self.bus.modules['life'].apply_options()

    def show_preview(self):
        self.error_field.setText('')
        self.update_options()
        try:
            if ((self.browse.filepath.text() != '')
                    and (not self.spec_kind.currentText() == 'contrast')):
                self.spec_import.do_import(pathtotext=self.browse.filepath.text(),
                                           x_string=self.x_units.box.text(),
                                           y_string=self.y_units.box.text(),
                                           distance_s_spectrum=self.distance_spec.value(),
                                           distance_s_target=self.distance_s.value(),
                                           radius_p_spectrum=self.radius_spec.value(),
                                           radius_p_target=self.radius_p.value(),
                                           integration_time=self.time_spec.value())

                self.flux_planet_spectrum = [self.spec_import.x_data, self.spec_import.y_data]
                if self.spec_kind.currentText() == 'additive':
                    widths = (self.spec_import.x_data.value[1:]
                              - self.spec_import.x_data.value[:-1])
                    widths = np.append(widths, widths[-1])
                    bins = self.spec_import.x_data.value + widths/2
                    fgamma = black_body(mode='planet',
                                        bins=bins,
                                        width=widths,
                                        temp=self.temp_p.value(),
                                        radius=self.radius_p.value(),
                                        distance=self.distance_s.value()) \
                             / widths \
                             * u.photon/u.second/(u.meter**3)
                    self.flux_planet_spectrum[1] += fgamma
                if self.prev_kind.currentIndex() == 0:
                    p_spec = [self.spec_import.x_raw, self.spec_import.y_raw]
                else:
                    p_spec = [self.flux_planet_spectrum[0].to(u.micron),
                              self.flux_planet_spectrum[1]]

            elif self.spec_kind.currentText() == 'additive':
                wl_edge = 1.
                wl_bins = []
                wl_bin_widths = []
                wl_bin_edges = [wl_edge]
                R = 200
                wl_max = 30

                while wl_edge < wl_max:

                    # set the wavelength bin width according to the spectral resolution
                    wl_bin_width = wl_edge / R / \
                                   (1 - 1 / R / 2)

                    # make the last bin shorter when it hits the wavelength limit
                    if wl_edge + wl_bin_width > wl_max:
                        wl_bin_width = wl_max - wl_edge

                    # calculate the center and edges of the bins
                    wl_center = wl_edge + wl_bin_width / 2
                    wl_edge += wl_bin_width

                    wl_bins.append(wl_center)
                    wl_bin_widths.append(wl_bin_width)
                    wl_bin_edges.append(wl_edge)

                # convert everything to [m]
                wl_bins = np.array(wl_bins) * 1e-6  # in m
                wl_bin_widths = np.array(wl_bin_widths) * 1e-6  # in m

                fgamma = black_body(mode='planet',
                                    bins=wl_bins,
                                    width=wl_bin_widths,
                                    temp=self.temp_p.value(),
                                    radius=self.radius_p.value(),
                                    distance=self.distance_s.value()) \
                         / wl_bin_widths \
                         * u.photon / u.second / (u.meter ** 3)

                self.flux_planet_spectrum = [wl_bins * u.meter, fgamma]
                p_spec = [self.flux_planet_spectrum[0].to(u.micron),
                          self.flux_planet_spectrum[1]]

            elif self.spec_kind.currentText() == 'contrast':
                self.spec_import.do_import(pathtotext=self.browse.filepath.text(),
                                           x_string=self.x_units.box.text(),
                                           y_string='photon s-1 m-3',
                                           distance_s_spectrum=None,
                                           distance_s_target=0,
                                           radius_p_spectrum=None,
                                           radius_p_target=0,
                                           integration_time=0)

                self.flux_planet_spectrum = [self.spec_import.x_data, self.spec_import.y_data]

                widths = (self.spec_import.x_data.value[1:]
                          - self.spec_import.x_data.value[:-1])
                widths = np.append(widths, widths[-1])
                bins = self.spec_import.x_data.value + widths / 2
                fgamma = black_body(mode='star',
                                    bins=bins,
                                    width=widths,
                                    temp=self.temp_s.value(),
                                    radius=self.radius_s.value(),
                                    distance=self.distance_s.value()) \
                         / widths \
                         * u.photon / u.second / (u.meter ** 3)
                self.flux_planet_spectrum[1] *= fgamma
                if self.prev_kind.currentIndex() == 0:
                    p_spec = [self.spec_import.x_raw, self.spec_import.y_raw]
                else:
                    p_spec = [self.flux_planet_spectrum[0].to(u.micron),
                              self.flux_planet_spectrum[1]]

            else:
                raise ValueError('Given file cannot be imported')


            self.p_plot.axes.cla()
            self.p_plot.axes.plot(p_spec[0], p_spec[1], color="darkblue", linestyle="-")
            if self.prev_kind.currentIndex() == 1:
                self.p_plot.axes.set_xlim(self.bus.data.options.array['wl_min'],
                                          self.bus.data.options.array['wl_max'])
            self.p_plot.axes.set_ylim(
                (5e-1 * np.min(
                    p_spec[1]
                    [int(np.argwhere(p_spec[0] <
                                     (self.bus.data.options.array['wl_min']*u.micron))[-1]):
                     int(np.argwhere(p_spec[0] >
                                     (self.bus.data.options.array['wl_max']*u.micron))[0])]).value),
                (0.5e1 * np.max(p_spec[1]
                                [int(np.argwhere(p_spec[0] <
                                                 (self.bus.data.options.array['wl_min']
                                                  *u.micron))[-1])
                                 :int(np.argwhere(p_spec[0] >
                                                  (self.bus.data.options.array['wl_max']
                                                   *u.micron))[0])])).value)

            self.p_plot.axes.set_yscale('log')
            self.p_plot.axes.grid()
            self.p_plot.axes.ticklabel_format(axis='x', style='sci')
            self.p_plot.draw()
        except:
            self.error_field.setText('An error occured during import')

    def show_spectrum(self,
                      spectrum,
                      planet,
                      noise):
        self.r_plot.axes.cla()
        self.r_plot.axes.step(spectrum[0] * 1e6, planet / (self.time_b.value()*60*60),
                              where="mid", color="r", label=f"Planet")
        self.r_plot.axes.step(spectrum[0] * 1e6, noise / (self.time_b.value()*60*60),
                              where="mid", color="black", label=f"Noise sources")
        self.r_plot.axes.set_xlabel(r"$\lambda$ [$\mu$m]")
        self.r_plot.axes.set_ylabel(r"Detected signal per bin [e$^-$ s$^{-1}$ bin$^{-1}$]")

        self.r_plot.axes.set_xlim(self.bus.data.options.array['wl_min']-0.5,
                                  self.bus.data.options.array['wl_max']+0.5)
        self.r_plot.axes.set_yscale('log')
        self.r_plot.axes.grid()

        ax2a = self.r_plot.axes.twinx()
        ax2a.set_yscale('log')
        ax2a.step(spectrum[0] * 1e6, spectrum[1],
                  where="mid", color="darkblue", linestyle="--",
                  label=f"SNR per bin \nTotal: {np.sqrt((spectrum[1] ** 2).sum()):.2f}")
        ax2a.set_ylabel('SNR per bin')
        lines, labels = self.r_plot.axes.get_legend_handles_labels()
        lines2, labels2 = ax2a.get_legend_handles_labels()
        ax2a.legend(lines + lines2, labels + labels2, framealpha=1)
        ax2a.grid(False)

        d_min = np.min(np.array((spectrum[1],
                                 planet / (self.time_b.value()*60*60),
                                 noise / (self.time_b.value()*60*60)))) * 1e-1
        d_max = np.max(np.array((spectrum[1],
                                 planet / (self.time_b.value()*60*60),
                                 noise / (self.time_b.value()*60*60)))) * 1e1
        self.r_plot.axes.set_ylim(d_min, d_max)
        ax2a.set_ylim(d_min, d_max)

        self.r_plot.draw()

        ax2a.remove()

    def run_simulation(self):
        self.show_preview()
        self.update_options()

        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            self.bus.disconnect(module_names=('life', 'sl'))
            self.bus.disconnect(module_names=('life', 'lz'))
            self.bus.disconnect(module_names=('life', 'ez'))
        if self.box_sl.isChecked():
            self.bus.connect(module_names=('life', 'sl'))
        if self.box_lz.isChecked():
            self.bus.connect(module_names=('life', 'lz'))
        if self.box_ez.isChecked():
            self.bus.connect(module_names=('life', 'ez'))

        self.r_spec, self.flux_p, self.flux_n = self.bus.modules['life'].get_spectrum(
            temp_s=self.temp_s.value(),
            radius_s=self.radius_s.value(),
            distance_s=self.distance_s.value(),
            flux_planet_spectrum=self.flux_planet_spectrum,
            lat_s=self.lat.value(),
            z=self.z.value(),
            angsep=self.angsep.value(),
            integration_time=self.time_b.value()*60*60)

        self.show_spectrum(self.r_spec,
                           self.flux_p,
                           self.flux_n)

    def set_values(self):
        self.diameter.set(self.bus.data.options.array['diameter'])
        self.spec_res.set(self.bus.data.options.array['spec_res'])
        self.wl_range.lower.setValue(self.bus.data.options.array['wl_min'])
        self.wl_range.upper.setValue(self.bus.data.options.array['wl_max'])

    def set_scenario_opt(self):
        self.bus.data.options.set_scenario(case='optimistic')
        self.set_values()

    def set_scenario_bas(self):
        self.bus.data.options.set_scenario(case='baseline')
        self.set_values()

    def set_scenario_pes(self):
        self.bus.data.options.set_scenario(case='pessimistic')
        self.set_values()

    def save_spectrum(self):
        if self.r_spec is not None:
            np.savetxt(fname=self.save.filepath.text(),
                       X=np.array([self.r_spec[0], self.r_spec[1], self.flux_p]).T,
                       header='Wavelength [m]   SNR per bin for 1h  Input flux')

    def change_visibility(self):
        if self.spec_kind.currentText() == 'additive':
            self.temp_p.show()
            self.temp_p.label.setText('Temperature Planet')
            self.x_units.show()
            self.y_units.show()
            self.distance_spec.show()
            self.radius_spec.show()
            self.time_spec.show()
        elif self.spec_kind.currentText() == 'absolute':
            self.temp_p.hide()
            self.x_units.show()
            self.y_units.show()
            self.distance_spec.show()
            self.radius_spec.show()
            self.time_spec.show()
        elif self.spec_kind.currentText() == 'contrast':
            self.temp_p.hide()
            self.temp_p.label.setText('Temperature Star')
            self.x_units.show()
            self.y_units.hide()
            self.distance_spec.hide()
            self.radius_spec.hide()
            self.time_spec.hide()


class Gui(object):
    def __init__(self):
        os.chdir(os.path.dirname(__file__))
        quantity_support()
        app = QApplication([])
        gallery = Frame()
        gallery.show()
        app.exec_()


if __name__ == '__main__':
    app = QApplication([])
    gallery = Frame()
    gallery.show()
    app.exec_()

    # <div style="color:darkred;"> <\div>
