Exemple #1
0
def graph_calibrated_spectrum(llist,
                              lmin=0,
                              lmax=720,
                              imin=0,
                              imax=1,
                              autoscale=True,
                              gridlines=True,
                              canvas_size=(800, 400),
                              plot_title='Spectrum',
                              multi_plot=False,
                              offset=0):
    """
    displays calibrated spectrum llist in separate window
    allows change of intensity scale and saving of resulting plot
    if no filename is given, result is saved as llist + '_plot.png'
    :param llist: filename of calibrated spectrum with extension .dat
    :param lmin, lmax: wavelength range, can be inverse
    :param imin, imax: intensity range
    :param autoscale: if True, range is determined automatically
    :param gridlines: if True, grid lines are shown
    :param canvas_size: size of image
    :param plot_title: title displayed at the top
    :param multi_plot: if True, multiple spectra can be selected and displayed
    :param offset: spacing between spectra in plot
    :return: p, imin, imax, caltext
    """

    # --------------------------------------------------------------
    def draw_spectrum(lcal, ical, lmin, lmax, color='blue'):
        for l0 in range(0, len(lcal)):
            if (lmax > lmin and lmin <= lcal[l0] <= lmax) or (
                    lmax < lmin and lmin >= lcal[l0] >= lmax):
                if l0:
                    graph.DrawLine((lcal[l0 - 1], ical[l0 - 1]),
                                   (lcal[l0], ical[l0]), color, 2)

    # --------------------------------------------------------------
    if llist:
        lcal, ical = np.loadtxt(llist, unpack=True, ndmin=2)
    mod_file = ''
    caltext = ''
    x = y = 0
    c_array = [
        'blue', 'green', 'red', 'black', 'grey', 'brown', 'blue', 'green',
        'red', 'black', 'grey', 'brown'
    ]
    if multi_plot:
        index = 0
        l_array = []
        i_array = []
        f_array = []
        spec_list = sg.popup_get_file(
            'Select spectra for plotting',
            no_window=True,
            multiple_files=True,
            file_types=(
                ('Spectra', '*.dat'),
                ('ALL Files', '*.*'),
            ),
        )
        if spec_list:
            imin = 0
            imax = 0
            for spec in spec_list:
                lcal, ical = np.loadtxt(spec, unpack=True, ndmin=2)
                ical = ical + index * offset
                imin = min(imin, min(ical))
                imax = max(imax, max(ical))
                l_array.append(lcal)
                i_array.append(ical)
                f_array.append(spec)
                index += 1
            idelta = 0.05 * (imax - imin)
            imin -= idelta
            imax += idelta
    elif autoscale:
        lmin = lcal[0]
        lmax = lcal[len(lcal) - 1]
        imin = min(ical)
        imax = max(ical)
        idelta = 0.05 * (imax - imin)
        imin -= idelta
        imax += idelta
    points = []
    for l0 in range(len(lcal) - 1):
        points.append((lcal[l0], ical[l0]))
    # y coordinate autoscale
    # plotscale pixel/unit
    lscale = canvas_size[0] / (lmax - lmin)
    iscale = canvas_size[1] / (imax - imin)
    # layout with border for scales, legends
    layout = [[
        sg.Graph(canvas_size=canvas_size,
                 graph_bottom_left=(lmin - 40 / lscale, imin - 40 / iscale),
                 graph_top_right=(lmax + 10 / lscale, imax + 30 / iscale),
                 enable_events=True,
                 float_values=True,
                 background_color='white',
                 key='graph')
    ],
              [
                  sg.Button('Save', key='Save', bind_return_key=True),
                  sg.Button('Close Window', key='Close'),
                  sg.Text('Imin:'),
                  sg.InputText('', key='imin', size=(8, 1)),
                  sg.Text('Imax:'),
                  sg.InputText('', key='imax', size=(8, 1)),
                  sg.Button('Scale I', key='scaleI'),
                  sg.Text('Cursor Position: '),
                  sg.InputText('', size=(26, 1), key='cursor', disabled=True),
                  sg.Text('Scale Factor'),
                  sg.InputText('1.0', key='factor', size=(8, 1))
              ]]

    right_click_menu = [
        'unused',
        [
            'Multiply spectrum by factor', 'Divide Spectrum by factor',
            'Save modified spectrum', 'Normalize to peak value',
            'Compare with spectrum', 'Label Peak'
        ]
    ]

    window = sg.Window(llist,
                       layout,
                       keep_on_top=True,
                       right_click_menu=right_click_menu).Finalize()
    graph = window['graph']
    label_str, lam_calib = m_fun.create_line_list_combo('m_linelist',
                                                        window,
                                                        combo=False)

    # draw x-axis
    if lcal[0]:  # for raw spectrum lcal[0] = 0, otherwise lmin
        x_label = u'\u03BB' + ' [nm]'
    else:
        x_label = 'Pixel'
    # lamda = u'\u03BB'
    graph.DrawText(x_label, ((lmax + lmin) / 2, imin - 30 / iscale),
                   font='Arial 12')
    graph.DrawText(plot_title, ((lmax + lmin) / 2, imax + 15 / iscale),
                   font='Arial 12')
    # calculate spacing
    deltax = round((lmax - lmin) / 250) * 50
    const = 1
    while not deltax:
        const *= 10
        deltax = int(const * (lmax - lmin) / 250) * 50
    deltax /= const
    dmax = int(lmax / deltax) + 1
    dmin = int(lmin / deltax)
    for x in range(dmin, dmax):
        graph.DrawLine((x * deltax, imin - 3 / iscale), (x * deltax, imin))
        if gridlines:
            graph.DrawLine((x * deltax, imin), (x * deltax, imax), 'grey')
        graph.DrawText(x * deltax, (x * deltax, imin - 5 / iscale),
                       text_location=sg.TEXT_LOCATION_TOP,
                       font='Arial 10')

    # draw y-axis
    graph.DrawText('I', (lmin - 30 / lscale, (imin + imax) / 2),
                   font='Arial 12')
    # calculate spacing
    deltay = round((imax - imin) / 5)
    const = 1
    while not deltay:
        const *= 10
        deltay = int(const * (imax - imin) / 5)
    deltay /= const
    dmax = int(imax / deltay) + 1
    dmin = int(imin / deltay)
    for d in range(dmin, dmax):
        graph.DrawLine((lmin - 3 / lscale, d * deltay), (lmin, d * deltay))
        if gridlines:
            graph.DrawLine((lmin, d * deltay), (lmax, d * deltay), 'grey')
        graph.DrawText(d * deltay, (lmin - 5 / lscale, d * deltay),
                       text_location=sg.TEXT_LOCATION_RIGHT,
                       font='Arial 10')

    graph.DrawRectangle((lmin, imin), (lmax, imax), line_width=2)
    # draw graph
    if multi_plot:
        if index:
            for ind in range(index):
                if offset <= 0:
                    pos_y = 25 * (ind + 1)
                else:
                    pos_y = 25 * (index - ind)
                draw_spectrum(l_array[ind],
                              i_array[ind],
                              lmin,
                              lmax,
                              color=c_array[ind])
                graph.DrawText(f_array[ind],
                               (lmax - 20 / lscale, imax - pos_y / iscale),
                               text_location=sg.TEXT_LOCATION_RIGHT,
                               font='Arial 12',
                               color=c_array[ind])
    else:
        draw_spectrum(lcal, ical, lmin, lmax)
    while True:
        event, values = window.read()
        if event in (None, 'Close'):
            window.close()
            return mod_file, imin, imax, caltext

        elif event == 'graph':  # if there's a "Graph" event, then it's a mouse
            x, y = (values['graph'])
            window['cursor'].update(f'Lambda:{x:8.2f}  Int:{y:8.2f}')

        elif event == 'Save':
            window.Minimize()
            p, ext = path.splitext(llist)
            p += '_plot.png'
            filename, info = m_fun.my_get_file(
                p,
                save_as=True,
                file_types=(('Image Files', '*.png'), ('ALL Files', '*.*')),
                title='Save spectrum plot (.PNG)',
                default_extension='*.png',
            )
            window.Normal()
            time.sleep(1.0)
            if filename:
                p, ext = path.splitext(filename)
                p += '.png'
            save_element_as_file(window['graph'], p)
            info = f'spectrum {llist} plot saved as {str(p)}'
            logging.info(info)
            caltext += info + '\n'
            window.close()
            return mod_file, imin, imax, caltext
        elif event == 'scaleI':
            try:
                imin = float(values['imin'])
                imax = float(values['imax'])
                iscale = canvas_size[1] / (imax - imin)
                graph.change_coordinates(
                    (lmin - 40 / lscale, imin - 40 / iscale),
                    (lmax + 10 / lscale, imax + 30 / iscale))
                draw_spectrum(lcal, ical, lmin, lmax, color='red')
                graph.update()
            except Exception as e:
                sg.PopupError(f'invalid values for Imin, Imax, try again\n{e}',
                              keep_on_top=True)
        elif event in ('Multiply spectrum by factor',
                       'Divide Spectrum by factor'):
            try:
                factor = float(values['factor'])
                if event == 'Multiply spectrum by factor':
                    ical = ical * factor
                    info = f'spectrum {llist} multiplied by factor {factor}'
                else:
                    ical = ical / factor
                    info = f'spectrum {llist} divided by factor {factor}'
            except Exception as e:
                sg.PopupError(f'invalid value for Factor, try again\n{e}',
                              keep_on_top=True)
                info = 'invalid factor'
            caltext += info + '\n'
            logging.info(info)
            draw_spectrum(lcal, ical, lmin, lmax, color='red')
            graph.update()
        elif event == 'Save modified spectrum':
            window.Minimize()
            mod_file, info = m_fun.my_get_file(llist,
                                               title='Save modified spectrum',
                                               save_as=True,
                                               file_types=(
                                                   ('Spectrum Files', '*.dat'),
                                                   ('ALL Files', '*.*'),
                                               ))
            if mod_file:
                mod_file = m_fun.change_extension(mod_file, '.dat')
                np.savetxt(mod_file,
                           np.transpose([lcal, ical]),
                           fmt='%8.3f %8.5f')
                info = f'modified spectrum {llist} saved as {mod_file}'
                logging.info(info)
                caltext += info + '\n'
            window.Normal()
        elif event == 'Normalize to peak value':
            peak_int = max(ical)
            ical = ical / peak_int
            imin = -.1
            imax = 1.1
            mod_file = m_fun.change_extension(llist, 'N.dat')
            np.savetxt(mod_file, np.transpose([lcal, ical]), fmt='%8.3f %8.5f')
            info = f'spectrum normalized to peak intensity = {peak_int}\n' \
                   f'saved as {mod_file}'
            caltext += info
            logging.info(info)
            draw_spectrum(lcal, ical, lmin, lmax, color='red')
        elif event == 'Compare with spectrum':
            window.Minimize()
            comp_file, info = m_fun.my_get_file(llist,
                                                title='Compare with spectrum',
                                                save_as=False,
                                                file_types=(
                                                    ('Spectrum Files',
                                                     '*.dat'),
                                                    ('ALL Files', '*.*'),
                                                ))
            if comp_file:
                window.Normal()
                caltext += f'File {comp_file} loaded\n'
                lcomp, icomp = np.loadtxt(comp_file, unpack=True, ndmin=2)
                draw_spectrum(lcomp, icomp, lmin, lmax, color='red')
                graph.DrawText(llist, (lmax - 20 / lscale, imax - 15 / iscale),
                               text_location=sg.TEXT_LOCATION_RIGHT,
                               font='Arial 12',
                               color='blue')
                graph.DrawText(comp_file,
                               (lmax - 20 / lscale, imax - 40 / iscale),
                               text_location=sg.TEXT_LOCATION_RIGHT,
                               font='Arial 12',
                               color='red')
        elif event == 'Label Peak':
            layout_label = [[
                sg.InputText('Cursor',
                             size=(40, 1),
                             key='cursor',
                             disabled=True)
            ], [sg.InputText('', size=(40, 1), key='label')],
                            [sg.Button('Apply'),
                             sg.Button('Cancel')]]
            window_label = sg.Window('Label Peak',
                                     layout_label,
                                     keep_on_top=True).Finalize()
            for k in range(len(lam_calib)):
                if label_str[k][0] < x:
                    kk = k
            if kk < len(lam_calib):
                if abs(label_str[kk][0] - x) > abs(label_str[kk + 1][0] - x):
                    kk += 1
            window_label['label'].update(lam_calib[kk])
            klam = 0
            for k in range(len(lcal)):
                if lcal[k] < x:
                    klam = k
            i_peak = 0
            for k in range(max(0, klam - 10), min(klam + 10, len(lcal))):
                i_peak = max(i_peak, ical[k])
                lam_peak = label_str[kk][0]
            window_label['cursor'].update(
                f'Lambda:{lcal[klam]:8.2f}  Peak:{i_peak:8.2f}')
            while True:
                event, values = window_label.read()
                if event in 'Apply':
                    # check if label changed
                    new_label = values['label']
                    if new_label != lam_calib[kk]:
                        x = new_label.lstrip()
                        if len(x.split(' ', 1)) == 2:
                            (lam_peak, name) = x.split(' ', 1)
                        else:
                            window.Minimize()
                            window_label.close()
                            sg.PopupError(
                                ' type: wavelength (space) description, two values required '
                            )
                            window.Normal()
                            break
                        lam_peak = float(lam_peak)
                    if y > i_peak:
                        graph.DrawLine((lam_peak, i_peak + 20 / iscale),
                                       (lam_peak, y - 20 / iscale), 'black', 2)
                    else:
                        graph.DrawLine((lam_peak, i_peak - 20 / iscale),
                                       (lam_peak, y + 20 / iscale), 'black', 2)
                    graph.DrawText(new_label,
                                   location=(lam_peak, y),
                                   text_location=sg.TEXT_LOCATION_CENTER,
                                   font='Arial 12',
                                   color='black')
                if event in ('Cancel', None):
                    pass
                window_label.close()
                break
Exemple #2
0
def select_lines(infile, contrast, lines, res_dict, fits_dict, wloc, outfil):
    """
    displays new window with image infile + start + 'fit
    a rectangle around the selected line can be selected with dragging the mouse
    :param infile: filebase of image
    :param contrast: brightness of image
    :param lines: list of calibration wavelengths
    :param res_dict: dictionary
    :param fits_dict: "
    :param wloc: location of displayed window for selection
    :param outfil: filename without extension (.txt) with results of line selection
    :return:
    x0, y0: center coordinates of selected rectangle (int)
    dx, dy: half width and height of selected rectangle (int)
    """
    def fitgaussimage(image, xy0, dxy, lam):
        x0 = xy0[0]
        y0 = xy0[1]
        dx = dxy[0]
        dy = dxy[1]
        print(x0, y0, dx, dy)
        data = image[y0 - dy:y0 + dy, x0 - dx:x0 + dx]  # x - y reversed
        params, success = m_fun.fit_gaussian_2d(data)
        if success in [1, 2, 3, 4]:
            (height, x, y, width_x, width_y) = params  # x and y reversed
            width_x = 2 * np.sqrt(2 * np.log(2)) * np.abs(width_x)  # FWHM
            width_y = 2 * np.sqrt(2 * np.log(2)) * np.abs(width_y)  # FWHM
            x = x + y0 - dy  # y and x reversed
            y = y + x0 - dx
            xyw = (y, x, width_y, width_x, lam)  # x - y changed back
            return xyw
        else:
            return 0, 0, 0, 0, 0

    xyl = []
    dxy = [10, 10]
    i = i_plot = 0
    im, header = m_fun.get_fits_image(infile)
    if len(im.shape) == 3:
        imbw = np.sum(im, axis=2)  # used for fitgaussian(data)
    else:
        imbw = im
    # (ymax, xmax) = im.shape
    # print (xmax,ymax)
    m_fun.get_fits_keys(header, fits_dict, res_dict, keyprint=False)
    # #===================================================================
    # new rect_plt
    # first get size of graph from tmp.png and size of image
    # graph coordinates are in image pixels!
    (imy, imx) = im.shape[:2]
    image_file = 'tmp.png'  # scaled image
    imrescale = np.flipud(ios.imread(image_file))  # get shape
    (canvasy, canvasx) = imrescale.shape[:2]
    wlocw = (wloc[0], wloc[1])
    image_elem_sel = [
        sg.Graph(canvas_size=(canvasx, canvasy),
                 graph_bottom_left=(0, 0),
                 graph_top_right=(imx, imy),
                 key='-GRAPH-',
                 change_submits=True,
                 drag_submits=True)
    ]
    layout_select = [[
        sg.Ok(),
        sg.Cancel(),
        sg.Button('Skip Line'),
        sg.Button('Finish'),
        sg.Button('I'),
        sg.Button('D'),
        sg.Text(infile, size=(30, 1)),
        sg.Text(key='info', size=(40, 1))
    ], image_elem_sel]
    winselect = sg.Window(f'select rectangle  for fit size, click lines',
                          layout_select,
                          finalize=True,
                          location=wlocw,
                          keep_on_top=True,
                          no_titlebar=False,
                          resizable=True,
                          disable_close=False,
                          disable_minimize=True,
                          element_padding=(2, 2))
    # get the graph element for ease of use later
    graph = winselect['-GRAPH-']  # type: sg.Graph
    # initialize interactive graphics
    winselect_active = True
    img = graph.draw_image(image_file, location=(0, imy))
    dragging = False
    start_point = end_point = prior_rect = None
    x0 = y0 = 0
    index = 0
    icircle = itext = None
    color = 'yellow'
    while winselect_active:
        event, values = winselect.read()
        # print(event)
        dx = int((dxy[0] + 1) // 2)
        dy = int((dxy[1] + 1) // 2)
        if event == "-GRAPH-":  # if there's a "Graph" event, then it's a mouse
            x, y = (values["-GRAPH-"])
            if not dragging:
                start_point = (x, y)
                dragging = True
            else:
                end_point = (x, y)
            if prior_rect:
                graph.delete_figure(prior_rect)
            if None not in (start_point, end_point):
                prior_rect = graph.draw_rectangle(start_point,
                                                  end_point,
                                                  line_color='red')
        elif event is not None and event.endswith('+UP'):
            # The drawing has ended because mouse up
            xy0 = [
                int(0.5 * (start_point[0] + end_point[0])),
                int(0.5 * (start_point[1] + end_point[1]))
            ]
            size = (abs(start_point[0] - end_point[0]),
                    abs(start_point[1] - end_point[1]))
            info = winselect["info"]
            info.update(value=f"grabbed rectangle at {xy0} with size {size}")
            start_point, end_point = None, None  # enable grabbing a new rect
            dragging = False
            if min(size[0], size[1]) > 2:  # rectangle
                info.update(value=f"rectangle at {xy0} with size {size}")
                dxy = size
            elif i < len(lines):
                if prior_rect:
                    graph.delete_figure(prior_rect)
                x0 = xy0[0]
                y0 = xy0[1]
                print(xy0, lines[i])
                xyw = (fitgaussimage(imbw, xy0, dxy, lines[i]))
                if xyw[0]:  #successful fit
                    if 0 < xyw[0] < imx and 0 < xyw[1] < imy:
                        print(np.float16(xyw))
                        xyl.append(np.float32(xyw))
                        # Draw the click just made
                        r = (xyw[2] + xyw[3]) / 4
                        icircle = graph.DrawCircle((xyw[0], xyw[1]),
                                                   r,
                                                   line_color=color,
                                                   line_width=3)
                        itext = graph.DrawText(
                            '  ' + str(lines[i]),
                            location=(xyw[0], xyw[1]),
                            color=color,
                            font=('Arial', 12),
                            angle=45,
                            text_location=sg.TEXT_LOCATION_BOTTOM_LEFT)
                        # itext =graph.DrawText('+', location=(xyw[0], xyw[1]), color='yellow',
                        #         font=('Arial', 12), text_location=sg.TEXT_LOCATION_CENTER)
                        info.update(
                            value=f"line {lines[i]} at {np.float16(xyw)}")
                        graph.update()
                        i += 1
                        i_plot += 1
                    else:
                        info.update(value='bad fit, try again')
                        print('bad fit, try again')
                else:
                    info.update(value='Fit not successful, try again')
                    print('Fit not successful, try again')
            else:
                info.update(value='all lines measured, press OK or Cancel')

        elif event == 'Ok':
            if np.array(xyl).shape[0] > 1:
                # minimum of two lines needed for fit
                xyl = np.array(xyl, dtype=np.float32)  # for ordered output
                with open(m_fun.change_extension(outfil, '.txt'), 'ab+') as f:
                    np.savetxt(f,
                               xyl,
                               fmt='%8.2f',
                               header=str(index) + ' ' + str(infile) + '.fit')
                    np.savetxt(f, np.zeros((1, 5)), fmt='%8.2f')
                index += 1
                color = 'red' if color == 'yellow' else 'yellow'  # alternate colors for spectra
            elif icircle:
                graph.delete_figure(icircle)  # last point
                graph.delete_figure(itext)
                graph.update()
            xyl = []
            i = i_plot = 0

        elif event == 'Cancel':
            for ind in range(i_plot):
                xyl = np.array(xyl, dtype=np.float32)  # for ordered output
                rsq2 = (xyl[ind, 2] + xyl[ind, 3]) / 5.6
                drag_figures = graph.get_figures_at_location(
                    (xyl[ind, 0] + rsq2, xyl[ind, 1] + rsq2))
                for figure in drag_figures:
                    if figure != img:
                        graph.delete_figure(figure)
            graph.update()
            xyl = []
            i = i_plot = 0

        elif event == 'Skip Line':
            i += 1  # do not increment iplot!

        elif event in ('I', 'D'):
            if event == 'I':
                contrast *= 2
            else:
                contrast /= 2
            im_tmp = imrescale / np.max(imrescale) * 255 * contrast
            im_tmp = np.clip(im_tmp, 0.0, 255)
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                ios.imsave(image_file, np.flipud(im_tmp).astype(np.uint8))
            graph.delete_figure(img)
            img = graph.draw_image(image_file, location=(0, imy))
            graph.send_figure_to_back(img)

        elif event in ('Finish', None):
            if event == 'Finish':
                with open(outfil + '.txt', 'ab+') as f:
                    np.savetxt(f, np.zeros((1, 5)), fmt='%8.2f')
                (x, y) = winselect.current_location()
                wlocw = (x, y)
            winselect.close()
            return wlocw
Exemple #3
0
def main():
    # -------------------------------------------------------------------------
    # default parameters of fit, use for Watec
    # read in from default ini-file, see m_specfun
    # -------------------------------------------------------------------------
    sg.SetGlobalIcon('Koji.ico')
    sg_ver = sg.version.split(' ')[0]
    print('PySimpleGUI', sg_ver)
    if int(sg_ver.split('.')[0]) >= 4 and int(sg_ver.split('.')[1]) >= 9:
        sg.change_look_and_feel(
            'SystemDefault')  # suppresses message in PySimpleGUI >= 4.9.0
    version = '0.8.6'
    pngdir = '_tmp_/cal_'
    # start with default ini_file
    ini_file = 'm_set.ini'
    par_text, par_dict, res_dict, fits_dict, opt_dict = m_fun.read_configuration(
        ini_file, m_fun.par_dict, m_fun.res_dict, m_fun.opt_dict)
    fits_dict['VERSION'] = version
    if par_text == '':
        sg.PopupError(
            f'no valid configuration found, default {ini_file} created')
        m_fun.write_configuration(ini_file, par_dict, res_dict, fits_dict,
                                  opt_dict)
    # [lam0, scalxy, fitxy, imx, imy, f0, pix, grat, rotdeg, binning, comment,
    #  infile, outfil,linelist,typesqrt] = list(par_dict.values())
    outfil = par_dict['s_outfil']
    parkey = list(par_dict.keys())
    # [zoom, wsx, wsy, wlocx, wlocy, xoff_calc, yoff_calc, xoff_setup, yoff_setup,
    #     debug, fit_report, win2ima, opt_comment, pngdir, png_name, outpath, mdist, colorflag, bob_doubler,
    #     plot_w, plot_h, i_min, i_max, graph_size, show_images] = list(opt_dict.values())
    wsize = (opt_dict['win_width'], opt_dict['win_height'])
    wloc = (opt_dict['win_x'], opt_dict['win_y'])
    debug = opt_dict['debug']
    notok, linelist, lines = lfun.get_linelist(par_dict['s_linelist'],
                                               par_dict['f_lam0'])
    if linelist:
        par_dict['s_linelist'] = linelist
    infile = par_dict['s_infile']
    # for new draw_scaled_image
    graph_size = opt_dict['graph_size']
    graph_s2 = (graph_size, graph_size)
    idg = None
    imbw = []

    # ------------------------------------------------------------------------------
    #   Start Menu Definition
    # ------------------------------------------------------------------------------
    menu_def = [
        ['File', ['Save Actual File', 'Exit']],
        # ['Tools', ['Offset', ['Special', 'Normal', ], 'Undo'], ],
        [
            'Tools',
            ['Offset', 'Edit Text File', 'Edit Log File'],
        ],
        ['Help', 'About...'],
    ]

    # elements of main window, which are updated:
    image_elem = sg.Graph(canvas_size=graph_s2,
                          graph_bottom_left=(0, 0),
                          graph_top_right=graph_s2,
                          key='image')

    log_elem = sg.Multiline('Log', size=(38, 12), autoscroll=True)
    filename_display_elem = sg.Text(infile,
                                    size=(100, 1),
                                    key='image_filename')

    # layout main window
    layout_parameters = sg.Frame(
        '',
        [[
            sg.Frame('Setup',
                     [[sg.Input(ini_file, size=(40, 1), key='setup_file')],
                      [sg.Button('Load Setup'),
                       sg.Button('Edit Setup')]])
        ],
         [
             sg.Frame('Video Extraction',
                      [[sg.Input('', size=(40, 1), key='avi_file')],
                       [
                           sg.Button('Load Avi'),
                           sg.Text('Calibration image:'),
                           sg.Button('Save Image')
                       ], [sg.Input('', size=(40, 1), key='image_file')]])
         ],
         [
             sg.Frame('Select Lines',
                      [[sg.Input(infile, size=(40, 1), key='input_file')],
                       [
                           sg.Button('Load Image'),
                           sg.Text('Calibration data, ".txt":')
                       ],
                       [
                           sg.Input(outfil,
                                    size=(40, 1),
                                    key='output_file',
                                    tooltip='select file for calibration data')
                       ],
                       [
                           sg.Button('Select File'),
                           sg.Button('Edit File'),
                           sg.Button('Select Lines')
                       ],
                       [
                           sg.Text('Linelist'),
                           sg.Input(linelist, size=(20, 1), key='linelist'),
                           sg.Button('Load L_list')
                       ]])
         ],
         [
             sg.Frame('Calibration', [[
                 sg.Button('LSQF'),
                 sg.Checkbox(
                     'SQRT-Fit', default=par_dict['b_sqrt'], key='SQRT-Fit'),
                 sg.Checkbox(
                     'Fit-xy', default=par_dict['b_fitxy'], key='fitxy')
             ]])
         ], [sg.Text('Results:')], [log_elem]])

    layout_image = [[sg.Menu(menu_def, tearoff=True)],
                    [
                        layout_parameters,
                        sg.Column([[filename_display_elem], [image_elem]])
                    ]]

    # ------------------------------------------------------------------------------
    #   Initialize Window
    # ------------------------------------------------------------------------------
    winsetup_active = False
    contr = 1
    logtext = 'Logfile ' + time.strftime("%Y%m%d_%H%M%S") + '\n'
    info = f'M_CALIB version {version}, lfun version {lfun.version}, m_fun.version {m_fun.version}'
    logtext += info + '\n'
    logging.info(info)
    current_dir = path.abspath('')
    window_title = f'M_CALIB, Version: {version}, {current_dir} , Image: '
    winmain = sg.Window(window_title,
                        layout_image,
                        location=wloc,
                        size=wsize,
                        resizable=True)
    winmain.read(timeout=0)
    image_data, idg, actual_file = m_fun.draw_scaled_image(
        'tmp.png', image_elem, opt_dict, idg)

    # ------------------------------------------------------------------------------
    #   Loop main window
    # ------------------------------------------------------------------------------
    while True:
        ev1, values = winmain.Read(timeout=100)

        if ev1 in (None, 'Exit'):
            if winsetup_active:
                winsetup.Close()
                del winsetup
            winmain.Close()
            del winmain
            if ev1 == 'Exit':
                m_fun.write_configuration('m_set.ini', par_dict, res_dict,
                                          fits_dict, opt_dict)
                logging.info('m_set.ini saved, M_CALIB FINISHED')
                logtext += 'm_set.ini saved\nM_CALIB FINISHED'
                with open('Log' + time.strftime("%Y%m%d_%H%M%S") + '.txt',
                          'w') as f:
                    f.write(logtext)
            break

        # adjust image size, update image if necessary
        if wsize != winmain.Size:
            wsize = winmain.Size
            opt_dict['win_width'] = wsize[0]
            opt_dict['win_height'] = wsize[1]
            # print('new widow size', wsize)
            image_data, idg, actual_file = m_fun.draw_scaled_image(
                actual_file, image_elem, opt_dict, idg, tmp_image=True)
        winmain.set_title(window_title + str(actual_file))
        log_elem.Update(logtext)

        # -------------------------------------------------------------------------------
        #       Video
        # -------------------------------------------------------------------------------
        if ev1 is 'Load Avi':
            avifile, info = m_fun.my_get_file(
                winmain['avi_file'].Get(),
                title='Get Video File',
                file_types=(('Video Files', '*.avi'), ('ALL Files', '*.*')),
                default_extension='.avi')
            logtext += info + '\n'
            if avifile:
                logging.info(f'start video conversion: {str(avifile)}')
                logtext += 'start video conversion: WAIT!\n'
                winmain['avi_file'].Update(avifile)
                winmain.refresh()
                nim, dattim, sta, out = m_fun.extract_video_images(
                    avifile,
                    pngdir,
                    bobdoubler=False,
                    binning=par_dict['i_binning'],
                    bff=False,
                    maxim=20)
                logging.info(f'finished video conversion: {str(avifile)}')
                logging.info(
                    f'nim: {str(nim)} date time: {dattim} station: {sta}')
                logtext += ('finished video conversion: ' + avifile + '\n' +
                            f'nim: {str(nim)} date time: {dattim} ' + '\n' +
                            f'station: {sta}' + '\n')
                print('nim:', nim, dattim, sta)
                imbw = m_fun.create_background_image(pngdir, nim)
                # save average image as png and fit
                lfun.save_fit_png('avi.png', imbw)
                # TODO: use function from m_fun or move save_fit_png
                image_data, idg, actual_file = m_fun.draw_scaled_image(
                    'avi.fit', image_elem, opt_dict, idg)

        if ev1 in ('Save Image', 'Save Actual File'):
            imfilename, info = m_fun.my_get_file(
                winmain['image_file'].Get(),
                title='Save image',
                file_types=(('Image Files', '*.fit'), ('ALL Files', '*.*')),
                save_as=True,
                default_extension='*.fit',
            )
            if imfilename:
                imfilename = m_fun.change_extension(imfilename, '')
                try:
                    lfun.save_fit_png(imfilename, imbw)
                    logtext += info + '\n'
                    winmain['image_file'].Update(imfilename)
                    winmain['image_filename'].Update(imfilename)
                except:
                    sg.PopupError('no video converted or image saved')
            else:
                'no image saved, missing filename'

# -------------------------------------------------------------------------------
#       Load, save image
# -------------------------------------------------------------------------------
        if ev1 == 'Load Image':
            files, info = m_fun.my_get_file(
                winmain['input_file'].Get(),
                title='Load image',
                file_types=(('Image Files', '*.fit'), ('PNG-File', '*.png'),
                            ('BMP-File', '*.bmp'), ('ALL Files', '*.*')),
                default_extension='*.fit',
                multiple_files=True)
            nim = len(files)
            new_infile = ''
            if nim == 0:
                sg.Popup('No file selected, keep last image')
                # imbw, opt_dict = lfun.load_image('tmp.png', opt_dict)
                image_data, idg, actual_file, imbw = m_fun.draw_scaled_image(
                    'tmp.png', image_elem, opt_dict, idg, get_image=True)
            else:
                # imbw, opt_dict = lfun.load_image(files[0], opt_dict)  # with extension
                image_data, idg, actual_file, imbw = m_fun.draw_scaled_image(
                    files[0],
                    image_elem,
                    opt_dict,
                    idg,
                    tmp_image=True,
                    get_image=True)
                infile = m_fun.m_join(files[0])
                if nim == 1:
                    if len(imbw):
                        if not files[0].lower().endswith(
                                '.fit'
                        ):  # further processing is with fits-images
                            error = m_fun.write_fits_image(
                                imbw,
                                m_fun.change_extension(infile, '.fit'),
                                fits_dict,
                                dist=False)
                            if error:
                                infile = ''
                            else:
                                logging.info(
                                    f'Load_Image: {infile} size: {str(imbw.shape)}'
                                )
                                logtext += 'Load_Image: ' + infile + ' size: ' + str(
                                    imbw.shape) + '\n'
                        new_infile = m_fun.change_extension(infile, '')
                    else:
                        sg.PopupError(' File not found or not read')
                        # imbw, opt_dict = lfun.load_image('tmp.png', opt_dict)
                elif nim > 1:
                    error = False
                    shape0 = imbw.shape
                    for file in files:
                        # imbw, tmp_dict = lfun.load_image(file, opt_dict, imagesave=False)
                        # load images to compare shape
                        # TODO: shorter version of load array only, no need to draw image
                        image_data, idg, actual_file, imbw = m_fun.draw_scaled_image(
                            file, image_elem, opt_dict, idg, get_image=True)

                        if imbw.shape != shape0:
                            sg.PopupError(
                                'all files must have the same format, try again!',
                                keep_on_top=True)
                            error = True
                            break
                    if not error:
                        for f in range(nim):
                            files[f] = path.relpath(files[f])
                            # im, opt_dict = lfun.load_image(files[f], opt_dict, imagesave=False)
                            # TODO: shorter version of load array only, no need to draw image
                            image_data, idg, actual_file, im = m_fun.draw_scaled_image(
                                files[f],
                                image_elem,
                                opt_dict,
                                idg,
                                get_image=True)
                            if f == 0:
                                imbw = im
                            else:
                                imbw = np.maximum(imbw, im)
                        new_infile = infile + '_peak_' + str(nim)
                        lfun.save_fit_png(new_infile, imbw)
                        image_data, idg, actual_file = m_fun.draw_scaled_image(
                            m_fun.change_extension(new_infile, '.fit'),
                            image_elem,
                            opt_dict,
                            idg,
                            tmp_image=True)
                        logging.info(
                            f'image saved as: {new_infile} (.fit, .png)')
                        logtext += 'Load_Images:' + '\n'
                        for f in range(nim):
                            logtext += files[f] + '\n'
                        logtext += f'image saved as: {new_infile}, .png)\n'
            if new_infile:
                infile = m_fun.m_join(new_infile)
                winmain['image_filename'].Update(infile)
                par_dict['s_infile'] = infile
                (imy, imx) = imbw.shape[:2]
                par_dict['i_imx'] = imx
                par_dict['i_imy'] = imy
            winmain['input_file'].Update(new_infile)

# -------------------------------------------------------------------------------
#       Select Lines
# ------------------------------------------------------------------------------
        if ev1 == 'Select File':
            old_outfil = winmain['output_file'].Get()
            outfil, info = m_fun.my_get_file(
                old_outfil,
                title='Load measured calibration lines file',
                file_types=(('Calibration Files', '*.txt'), ('ALL Files',
                                                             '*.*')),
                save_as=False,
                default_extension='*.txt')
            if not outfil:
                outfil = old_outfil
                if not outfil:
                    sg.PopupError('no File selected, try again')
            if outfil:
                outfil = m_fun.change_extension(outfil, '')
                outfil = m_fun.m_join(outfil)
                winmain['output_file'].Update(outfil)

        if ev1 == 'Load L_list':
            linelist, info = m_fun.my_get_file(
                winmain['linelist'].Get(),
                title='Get Linelist',
                file_types=(('Linelist', '*.txt'), ('ALL Files', '*.*')),
                default_extension='*.txt')
            if not linelist or linelist[:-1] == 'l':
                linelist = 'l'
            linelist = m_fun.change_extension(linelist, '')
            winmain['linelist'].Update(linelist)
            par_dict['s_linelist'] = linelist

        if ev1 == 'Select Lines':
            infile = winmain['input_file'].Get()
            outfil = winmain['output_file'].Get()
            if infile:
                if not Path(infile + '.fit').exists():
                    imbw, opt_dict = lfun.load_image(infile, opt_dict)
                # 'tmp.png' created with tmp_image=True, needed for sel.select_lines
                image_data, idg, actual_file = m_fun.draw_scaled_image(
                    m_fun.change_extension(infile, '.fit'),
                    image_elem,
                    opt_dict,
                    idg,
                    tmp_image=True,
                    get_image=False)
                p = Path(m_fun.change_extension(outfil, '.txt'))
                if not Path(p).exists():
                    if not p.parent.exists():
                        Path.mkdir(p.parent, exist_ok=True)
                        # p = path.relpath(Path.joinpath(p.parent, p.name).with_suffix('.txt'))
                        p = m_fun.m_join(Path.joinpath(p.parent, p.name),
                                         '.txt')
                    outfil = str(Path(p).with_suffix(''))
                    par_dict['s_outfil'] = outfil
                notok, linelist, lines = lfun.get_linelist(
                    par_dict['s_linelist'], par_dict['f_lam0'])
                winmain.Disable()
                sel.select_lines(infile, contr, lines, res_dict, fits_dict,
                                 wloc, outfil)
                winmain.Enable()
                winmain.BringToFront()
                logging.info(f'Finished, saved {outfil}.txt')
                logtext += ('Finished, saved ' + outfil + '.txt\n')
            else:
                sg.PopupError('no image selected, try again')

# ------------------------------------------------------------------------------
#       LSQ-Fit
# ------------------------------------------------------------------------------
        if ev1 == 'LSQF':
            outfil = winmain['output_file'].Get()
            if Path(m_fun.change_extension(outfil, '.txt')).exists():
                winmain['output_file'].update(outfil)
                par_dict['s_outfil'] = outfil
                par_dict['b_sqrt'] = winmain['SQRT-Fit'].Get()
                par_dict['b_fitxy'] = winmain['fitxy'].Get()
                parv = list(par_dict.values())
                logging.info(f'outfil: {outfil} START LSQF')
                logtext += 'outfil: ' + outfil + '\n'
                logtext += 'START LSQF ...\n'
                winmain.refresh()
                try:
                    par, result = lfun.lsqf(parv,
                                            debug=debug,
                                            fit_report=opt_dict['fit-report'])
                    (scalxy, x00, y00, rotdeg, disp0, a3, a5, errorx,
                     errory) = par
                    image_data, idg, actual_file = m_fun.draw_scaled_image(
                        outfil + '_lsfit.png', image_elem, opt_dict, idg)
                    rot = rotdeg * np.pi / 180
                    resv = np.float32([scalxy, x00, y00, rot, disp0, a3, a5])
                    reskey = [
                        'scalxy', 'x00', 'y00', 'rot', 'disp0', 'a3', 'a5'
                    ]
                    reszip = zip(reskey, resv)
                    res_dict = dict(list(reszip))
                    # write default configuration as actual configuration
                    m_fun.write_configuration('m_cal.ini', par_dict, res_dict,
                                              fits_dict, opt_dict)
                    logging.info('Result LSQF:')
                    logging.info(result)  # more detailed info
                    logtext += 'Result LSQF saved as m_cal.ini:\n'
                    logtext += result
                    print('END OF LSQ-Fit!!!')
                    logtext += 'END OF LSQ-Fit!\n'
                except:
                    sg.PopupError(f'Error in LSQ-fit, wrong {outfil} ?')
                    result = ' Error with: ' + str(outfil) + '.txt'
                    logging.info(result)
                    result += '\n----------------------------------------\n'
                    logtext += result
            else:
                sg.PopupError('no such file: ' + outfil + '.txt')

# ------------------------------------------------------------------------------
#       Setup
# ------------------------------------------------------------------------------
        if ev1 in ('Load Setup', 'Edit Setup') and not winsetup_active:
            print(ev1)
            if ev1 == 'Load Setup':
                ini_file, info = m_fun.my_get_file(
                    winmain['setup_file'].Get(),
                    title='Get Configuration File',
                    file_types=(('Configuration Files', '*.ini'), ('ALL Files',
                                                                   '*.*')),
                    default_extension='*.ini')
                par_text, par_dict, res_dict, fits_dict, opt_dict = m_fun.read_configuration(
                    ini_file, m_fun.par_dict, m_fun.res_dict, m_fun.opt_dict)
                fits_dict['VERSION'] = version
                if par_text == '':
                    sg.PopupError(
                        f'no valid configuration found, use current configuration',
                        keep_on_top=True)
            else:  # edit conf, update values from main menu
                par_dict['s_infile'] = winmain['input_file'].Get()
                par_dict['s_outfil'] = winmain['output_file'].Get()
                par_dict['s_linelist'] = winmain['linelist'].Get()
                par_dict['b_sqrt'] = winmain['SQRT-Fit'].Get()
                par_dict['b_fitxy'] = winmain['fitxy'].Get()
            parv = list(par_dict.values())
            winsetup_active = True
            winmain.Disable()
            wloc_setup = (wloc[0] + opt_dict['setup_off_x'],
                          wloc[1] + opt_dict['setup_off_y'])
            # update values of setup window
            input_row = []
            input_elem = []
            debug = opt_dict['debug']
            # if debug: print('setup parv:', parv)
            for k in range(15):
                input_elem.append(sg.Input(parv[k], size=(30, 1)))
                input_row.append(
                    [sg.Text(parkey[k], size=(10, 1)), input_elem[k]])
            filename_ini_in_elem = sg.InputText(ini_file, size=(34, 1))
            # layout of setup window
            zoom_elem = sg.Input(str(opt_dict['zoom']),
                                 key='zoom',
                                 size=(7, 1))
            headings = ['Parameter', 'Value']
            header = [[sg.Text(h, size=(7, 1)) for h in headings]]
            input_rows = [[sg.Text('Zoom', size=(5, 1)), zoom_elem]]
            ind = 9  # current index of next list element
            checkbox = [[
                sg.Checkbox('debug',
                            default=opt_dict['debug'],
                            pad=(10, 0),
                            key='debug')
            ],
                        [
                            sg.Checkbox('fit-report',
                                        default=opt_dict['fit-report'],
                                        pad=(10, 0),
                                        key='fit-report')
                        ],
                        [
                            sg.Checkbox('scale_win2ima',
                                        default=opt_dict['scale_win2ima'],
                                        pad=(10, 0),
                                        key='scale_win2ima')
                        ]]
            ind += 3
            layout_options = sg.Frame(
                'Options', header + input_rows + checkbox +
                [[sg.Text('Comment', size=(10, 1))],
                 [
                     sg.InputText(
                         opt_dict['comment'], size=(16, 1), key='comment')
                 ]])

            # Parameters
            layout_setup = [[
                sg.Frame(
                    'Settings',
                    [[
                        sg.Frame(
                            'Lasercal',
                            [
                                [sg.Text('Lasercal')],
                                # [[sg.Text(ki[k], size=(5,1)), sg.Input(kval[k])] for k in range(15)],
                                input_row[0],
                                input_row[1],
                                input_row[2],
                                input_row[3],
                                input_row[4],
                                input_row[5],
                                input_row[6],
                                input_row[7],
                                input_row[8],
                                input_row[9],
                                input_row[10],
                                input_row[11],
                                input_row[12],
                                input_row[13],
                                input_row[14],
                                # [input_row[k] for k in range(15)], does not work
                                [filename_ini_in_elem],
                                [
                                    sg.Button('SaveC', size=(6, 1)),
                                    sg.Button('Apply', size=(6, 1)),
                                    sg.Button('Cancel', size=(6, 1))
                                ]
                            ]),
                        layout_options
                    ]])
            ]]

            winsetup = sg.Window('Parameters',
                                 layout_setup,
                                 disable_close=True,
                                 disable_minimize=True,
                                 location=wloc_setup,
                                 keep_on_top=True,
                                 no_titlebar=False,
                                 resizable=True)

        while winsetup_active:
            evsetup, valset = winsetup.Read(timeout=100)
            if evsetup is 'Cancel':
                winsetup_active = False
                winmain.Enable()
                winsetup.Close()

            if evsetup in ('Apply', 'SaveC'):
                for k in range(15):
                    key = parkey[k]
                    if key[0] == 'b':
                        if valset[k] == '0':
                            par_dict[key] = False
                        else:
                            par_dict[key] = True
                    elif key[0] == 'i':
                        par_dict[key] = int(valset[k])
                    elif key[0] == 'f':
                        par_dict[key] = float(valset[k])
                    else:
                        par_dict[key] = valset[k]
                    input_elem[k].Update(valset[k])
                infile = par_dict['s_infile']
                if infile:
                    # imbw, opt_dict = lfun.load_image(infile, opt_dict)
                    image_data, idg, actual_file, imbw = m_fun.draw_scaled_image(
                        m_fun.change_extension(infile, '.fit'),
                        image_elem,
                        opt_dict,
                        idg,
                        tmp_image=True,
                        get_image=True)
                    if not image_data:
                        imbw = []
                else:
                    imbw = []
                if len(imbw):
                    par_dict['s_infile'] = str(infile)
                    winmain['input_file'].Update(infile)
                    winmain['image_filename'].Update(infile)
                else:
                    sg.PopupError(
                        f'Image {infile} not found, load tmp.png instead:',
                        keep_on_top=True)
                    # imbw, opt_dict = lfun.load_image('tmp.png', opt_dict)
                    image_data, idg, actual_file, imbw = m_fun.draw_scaled_image(
                        'tmp.png', image_elem, opt_dict, idg, get_image=True)
                winmain['setup_file'].Update(ini_file)
                winmain['output_file'].Update(par_dict['s_outfil'])
                winmain['SQRT-Fit'].Update(par_dict['b_sqrt'])
                winmain['fitxy'].Update(par_dict['b_fitxy'])
                # parv = list(par_dict.values())
                wloc = winmain.current_location()
                (x, y) = winsetup.current_location()
                opt_dict['setup_off_x'] = x - wloc[0]
                opt_dict['setup_off_y'] = y - wloc[1]
                opt_dict['zoom'] = float(valset['zoom'])
                opt_dict['debug'] = valset['debug']
                opt_dict['fit-report'] = valset['fit-report']
                opt_dict['scale_win2ima'] = valset['scale_win2ima']
                opt_dict['comment'] = valset['comment']
                notok = True
                while notok:
                    notok, linelist, lines = lfun.get_linelist(
                        par_dict['s_linelist'], par_dict['f_lam0'])
                    if notok:
                        linelist, info = m_fun.my_get_file(
                            winmain['linelist'].Get(),
                            title='Get Linelist',
                            file_types=(('Linelist', '*.txt'), ('ALL Files',
                                                                '*.*')),
                            default_extension='*.txt')
                        if not linelist:
                            linelist = 'l'
                            notok = False
                par_dict['s_linelist'] = str(Path(linelist).with_suffix(''))
                winmain['linelist'].Update(par_dict['s_linelist'])
                # parv[13] = par_dict['s_linelist']
                # input_elem[13].Update(parv[13])

                if evsetup in 'SaveC':
                    winsetup.Hide()
                    ini_file, info = m_fun.my_get_file(
                        filename_ini_in_elem.Get(),
                        title='Save Configuration File',
                        save_as=True,
                        file_types=(('Configuration Files', '*.ini'),
                                    ('ALL Files', '*.*')),
                        default_extension='*.ini',
                        error_message='no configuration saved: ')
                    if ini_file:
                        m_fun.write_configuration(ini_file, par_dict, res_dict,
                                                  fits_dict, opt_dict)
                    else:
                        sg.Popup('No file saved', keep_on_top=True)
                logtext += info + '\n'
                winsetup_active = False
                winmain.Enable()
                winsetup.Close()

# ------------------------------------------------------------------------------
        if ev1 == 'Edit Log File':
            m_fun.edit_text_window(m_fun.logfile, select=False, size=(90, 30))

# ------------------------------------------------------------------------------
        if ev1 == 'About...':
            # sg.Popup('Calibration of meteor spectra with grating\n' +
            #     'mounted perpendicular to optical axis\n' +
            #     'see:\nhttps://meteorspectroscopy.org/welcome/documents/\n\n' +
            #     'Martin Dubs, 2019', title='About')
            m_fun.about(version, program='M_Calib')


# ------------------------------------------------------------------------------
        if ev1 == 'Offset':
            if infile:
                if Path(m_fun.change_extension(infile, '.fit')).exists():
                    # imbw, opt_dict = lfun.load_image(infile, opt_dict)
                    image_data, idg, actual_file, imbw = m_fun.draw_scaled_image(
                        m_fun.change_extension(infile, '.fit'),
                        image_elem,
                        opt_dict,
                        idg,
                        get_image=True)
                    print('orig', np.min(imbw), np.max(imbw), np.average(imbw))
                    im = imbw - np.average(imbw)
                    im_std = np.std(im)
                    im_clip = np.clip(im, -2.0 * im_std, 2.0 * im_std)
                    offset = -np.average(imbw) - np.average(im_clip)
                    print('off', np.min(imbw), np.max(imbw), np.average(imbw))
                    imbw = np.clip(imbw + offset, 0.0, 1.0)
                    file_offset = m_fun.change_extension(infile, '_off.fit')
                    print('clip', np.min(imbw), np.max(imbw), np.average(imbw))
                    m_fun.write_fits_image(imbw, file_offset, fits_dict)
                    # imbw, opt_dict = lfun.load_image(file_offset, opt_dict)
                    # image_elem.update('tmp.png')
                    image_data, idg, actual_file = m_fun.draw_scaled_image(
                        m_fun.change_extension(infile, '_off.fit'),
                        image_elem,
                        opt_dict,
                        idg,
                        tmp_image=True)
                    infile = infile + '_off'
                    winmain['image_filename'].Update(infile)
                    winmain['input_file'].Update(infile)
                    logging.info(f'image with offset saved as: {file_offset}')
                    logtext += f'image with offset saved as: {file_offset}\n'
                    winmain.refresh()
                else:
                    sg.PopupError('file not found, load valid fits image')
        if ev1 in ('Edit Text File', 'Edit File'):
            outfil = winmain['output_file'].Get()
            m_fun.edit_text_window(outfil, size=(50, 30))

    return par_dict