"""Functions for GUI event handling.
Contains most functions for handling events in GUI. This includes controlling user events,
calls to PyTIE, alignment calls to FIJI, saving images, creating image masks,
and image manipulation for GUI display.
AUTHOR:
Timothy Cote, ANL, Fall 2019.
"""
# Standard library imports
import collections
from contextlib import redirect_stdout
from io import StringIO
from os import path as os_path, remove as os_remove, mkdir
from platform import system as platform
from queue import Queue, Empty
import subprocess
import shlex
from sys import path as sys_path, stdout as sys_stdout
from threading import Thread
from typing import Any, Dict, List, Optional, Tuple, Union
import webbrowser
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
# Third-party imports
from numpy import setdiff1d
import PySimpleGUI as sg
from matplotlib import colors
# Local imports
sys_path.append("../PyTIE/")
from gui_layout import window_ly, file_choice_ly, save_window_ly, output_ly, element_keys
from gui_styling import WindowStyle, get_icon
from colorwheel import colorwheel_HSV, colorwheel_RGB, color_im
from microscopes import Microscope
from TIE_helper import *
from TIE_reconstruct import TIE, SITIE, save_results
import util
from util import Struct, check_setup
# import faulthandler
# faulthandler.enable()
# ============================================================= #
# Setting defaults for FIJI and the working Directory. #
# ============================================================= #
[docs]def defaults() -> Dict[str, str]:
"""Load the default Fiji and working directory if any is set.
Returns:
DEFAULTS: Dictionary of the working directory paths.
"""
GUI_dir = os.path.dirname(os.path.abspath(__file__))
default_txt = f'{GUI_dir}/defaults.txt'
DEFAULTS = {'browser_dir': '', 'fiji_dir': ''}
if not os_path.exists(default_txt):
with open(default_txt, 'w+') as f:
f.write('// File contains the default paths to FIJI (ignore) and the browser working directory for GUI.\n')
f.write('FIJI Directory,\n')
f.write('Browser Directory,\n')
else:
try:
new_mode = os.stat(my_file).st_mode | 0o777
os.chmod(default_txt, new_mode)
except:
pass
with open(default_txt, 'r') as f:
for line in f.readlines():
if not line.startswith('//'):
items = line.split(',')
key, value = items[0], items[1]
value = value.strip()
if key == 'FIJI Directory':
DEFAULTS['fiji_dir'] = value
elif key == 'Browser Directory':
DEFAULTS['browser_dir'] = value
return DEFAULTS
# ============================================================= #
# ========== Window Functionality and Event Handling ========== #
# ============================================================= #
# ------------- Initialize and reset ------------- #
[docs]def init_rec(winfo: Struct, window: sg.Window, mask_reset: bool = True,
arrow_reset: bool = True) -> None:
"""Initialize Reconstruction Tab variables.
Initializes winfo arguments for the reconstruction tab this includes:
- The image working directory
- Loaded image/stack dictionary
- The last enabled/disabled element list to see if enabling/disabling needs to change,
this helps to prevent constantly enabling/disabling on every window call.
- The list of linear stack files
* ls_files1 is for unflip or tfs folder
* ls_files2 is for flip folder (when applicable)
- The fls files
- Tracking setting for the PYTIE parameters and object instances
- Trackers for the threads that are run or currently running for PYTIE
initialization and reconstruction
- The last selected stack/image choice for viewing.
- Trackers for the stack and image choice sliders
- Dictionary of image choices available to view
- The transformation adjustments to apply to the non-reference image
- Trackers for the vector maps on the magnetic images
- Timers to update the transformation adjustment
- Arguments for tracking mask/ROI making and the corners/location of the mask/ROI.
Args:
winfo: A data structure that holds a information about
window and GUI.
window: The main element that represents the GUI window.
mask_reset: Whether to reset the region select parameters.
arrow_reset: Whether to reset the arrow parameters.
Returns:
None
"""
# Declare image path and image storage
winfo.rec_image_dir = ''
winfo.rec_images = {}
winfo.last_rec_disable, winfo.last_rec_enable = None, None
winfo.rec_fls_files = [None, None]
winfo.rec_files1 = None
winfo.rec_files2 = None
# PTIE parameters and elements
winfo.rec_ptie = None
winfo.rec_sym = None
winfo.rec_qc = None
winfo.rec_microscope = None
winfo.rec_colorwheel = None
winfo.rec_past_recon_thread = None
# --- Set up loading files --- #
winfo.rec_defocus_slider_set = 0
winfo.rec_image_slider_set = 7
winfo.rec_image_slider_dict = {'Stack': 0, 'Color': 1, 'Vector Im.': 2,
'MagX': 3, 'MagY': 4, 'Mag': 5,
'Electr. Phase': 6, 'Mag. Phase': 7,
'Electr. Deriv.': 8, 'Mag. Deriv.': 9,
'In Focus': 10, 'Loaded Stack': 11}
# Declare transformation timers and related variables
if arrow_reset:
winfo.rec_past_arrow_transform = (15, 1, 1, 'On')
# Image selection
winfo.rec_last_image_choice = None
winfo.rec_last_colorwheel_choice = None
winfo.rec_tie_results = None
winfo.rec_def_val = None
# Transformations
# Graph and mask making
graph_size = window['__REC_Graph__'].metadata['size']
winfo.rec_mask_center = ((graph_size[0]) / 2, (graph_size[1]) / 2)
# Declare rectangular selection of reconstruction region
winfo.rec_rotxy_timers = (0, 0, 0)
winfo.rec_corner1 = None
winfo.rec_corner2 = None
winfo.new_selection = True
winfo.graph_slice = (None, None)
winfo.rec_graph_double_click = False
winfo.rec_pad_info = (None, 0, 0, 0)
winfo.rec_mask_coords = []
winfo.rec_mask_markers = []
if mask_reset:
winfo.rec_transform = (0, 0, 0, None)
winfo.rec_past_transform = (0, 0, 0, None)
winfo.rec_mask_timer = (0,)
winfo.rec_mask = (50,)
winfo.rec_past_mask = (50,)
[docs]def init(winfo: Struct, window: sg.Window, output_window: sg.Window) -> None:
"""The main element and window initialization. Creates all initial bindings.
Initializes winfo arguments for the main GUI, includeing:
- The window element
- The output log window element
- Arguments for tracking the active window
- The keys available for elements in the window
- Tracks which tab is open
- Holds the buffer for printing output to log
- Managers for reconstruction threads and FIJI threads (and all processes)
- Tracks what paths are stored for the users defaults
- Managers for which loading icons should be displayed
- Tracks which element should have focus in the window
Args:
winfo: A data structure that holds a information about
window and GUI.
window: The main element that represents the GUI window.
output_window: The main element that represents the Log
output Window.
Returns:
None
"""
# --- Set up window and tabs --- #
winfo.window = window
winfo.output_window = output_window
winfo.window_active = True
winfo.output_window_active = False
winfo.output_focus_active = False
winfo.active_output_focus_el = None
winfo.last_browser_color = None
keys = element_keys()
winfo.keys = keys
winfo.invis_graph = window.FindElement("__invisible_graph__")
winfo.output_invis_graph = output_window.FindElement("__output_invis_graph__")
winfo.tabnames = ["Home", "Phase Reconstruction"]
winfo.pages = "pages_tabgroup"
winfo.current_tab = "home_tab"
winfo.buf = None
winfo.ptie_init_thread = None
winfo.ptie_recon_thread = None
winfo.rec_tie_prefix = 'Example'
winfo.ptie_init_spinner_active = False
winfo.ptie_recon_spinner_active = False
# --- Set up bUnwarpJ tab --- #
init_rec(winfo, window)
# --- Set up event handling and bindings --- #
winfo.true_element = None
winfo.window.bind("<Button-1>", 'Window Click')
winfo.output_window.bind("<Button-1>", 'Log Click')
winfo.window['__REC_Graph__'].bind('<Double-Button-1>', 'Double Click')
winfo.window.bind("<Control-l>", 'Show Log')
winfo.window.bind("<Control-h>", 'Hide Log')
winfo.output_window.bind("<Control-h>", 'Output Hide Log')
big_list = keys['input'] + keys['radio'] + keys['graph'] + keys['combo'] + \
keys['checkbox'] + keys['slider'] + keys['button'] + keys['listbox']
for key in big_list:
winfo.window[key].bind("<Enter>", '+HOVER+')
winfo.window[key].bind("<Leave>", '+STOP_HOVER+')
for key in ['MAIN_OUTPUT']:
winfo.output_window[key].bind("<FocusIn>", '+FOCUS_IN+')
winfo.output_window[key].bind("<FocusOut>", '+FOCUS_OUT+')
[docs]def reset(winfo: Struct, window: sg.Window, current_tab: str) -> None:
"""Reset the current tab elements to default values.
Args:
winfo: A data structure that holds the information about the
window and GUI.
window: The main representation of the GUI window.
current_tab: The key of the current tab being viewed in the window.
Returns:
None
"""
# Reset tabs
if current_tab == 'reconstruct_tab':
graph = window['__REC_Graph__']
colorwheel_graph = window['__REC_Colorwheel_Graph__']
graph.Erase()
colorwheel_graph.Erase()
metadata_change(winfo, window, ['__REC_Stack__', '__REC_Image__', '__REC_FLS1__', '__REC_FLS2__'], reset=True)
toggle(winfo, window, ['__REC_Set_Img_Dir__', '__REC_FLS_Combo__', '__REC_TFS_Combo__',
'__REC_Stack__', '__REC_FLS1__', '__REC_FLS2__', '__REC_Set_FLS__',
'__REC_Mask__', '__REC_Image__'], state='Def')
window['__REC_Def_Combo__'].update(values=['None'])
window['__REC_Def_List__'].update(values=['None'])
window['__REC_FLS1_Text__'].update(value=window['__REC_FLS1_Text__'].metadata['Two'])
window['__REC_FLS2_Text__'].update(value=window['__REC_FLS2_Text__'].metadata['Two'])
window['__REC_FLS1_Text__'].metadata['State'] = 'Two'
window['__REC_FLS2_Text__'].metadata['State'] = 'Two'
change_list_ind_color(window, current_tab, [('__REC_Image_List__', [])])
change_inp_readonly_bg_color(window, ['__REC_Stack__', '__REC_FLS1__',
'__REC_FLS2__',
'__REC_QC_Input__', '__REC_Arrow_Num__',
'__REC_Arrow_Len__', '__REC_Arrow_Wid__'], 'Readonly')
update_values(winfo, window, [('__REC_Image_Dir_Path__', ""), ('__REC_Image__', 'None'),
('__REC_Stack_Stage__', ''), ('__REC_FLS1_Staging__', ''),
('__REC_FLS2_Staging__', ''),
('__REC_Def_Combo__', 'None')])
init_rec(winfo, window, mask_reset=False, arrow_reset=False)
update_slider(winfo, window, [('__REC_Defocus_Slider__', {'value': winfo.rec_defocus_slider_set,
'slider_range': (0, 0)}),
('__REC_Slider__', {'value': 0, 'slider_range': (0, 0)}),
('__REC_Image_Slider__', {'value': winfo.rec_image_slider_set})])
window['__REC_Image_List__'].update(set_to_index=0, scroll_to_index=0)
window['__REC_Def_List__'].update(set_to_index=0, scroll_to_index=0)
# ------------- Window Helper Functions ------------- #
[docs]def shorten_name(path: str, ind: int = 1) -> str:
"""Creates a string of the path name with only the direct parent
"image_dir" and the child of "image_dir".
Args:
path: The full_path to be shortened.
ind: The index for checking how many '/' to check in path.
Returns:
shortname: The shortened pathname for window display.
"""
check_string = path
for i in range(ind):
index = check_string.rfind('/') - 1
check_string = check_string[:index]
shortname = path[index+2:]
return shortname
[docs]def get_open_tab(winfo: Struct, tabgroup: str, event: str) -> str:
"""Recursively determine which tab is open.
Args:
winfo: The data structure holding all information about
windows and GUI.
tabgroup: The key of the tabgroup.
event: The event key.
Returns:
tab_key: The key for the current open tab.
"""
# Recursively go through tabgroups and tabs to find
# current tab.
tab_key = winfo.window[tabgroup].Get()
tab = winfo.window[tab_key]
tab_dict = tab.metadata
child_tabgroup = None
if tab_dict is not None:
child_tabgroup = tab_dict["child_tabgroup"]
if child_tabgroup is not None:
tab_key = get_open_tab(winfo, child_tabgroup, event)
return tab_key
if 'tab' in event:
if tab_key == "home_tab":
tab = 'Home'
elif tab_key == "reconstruct_tab":
tab = 'Reconstruct'
if event not in ['output_tabgroup', 'param_tabgroup']:
print(f'*** Current Tab: {tab} ***')
return tab_key
[docs]def get_orientation(winfo: Struct, window: sg.Window, pref: str) -> str:
"""Get the current orientation value for the
current window.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
pref: The prefix for the the key of the orientation for the window.
Returns:
orientation: The orientation the current image should be.
"""
if window[f'__{pref}_unflip_reference__'].Get():
orientation = 'unflip'
elif window[f'__{pref}_flip_reference__'].Get():
orientation = 'flip'
else:
orientation = 'tfs'
if not os_path.exists(util.join([image_dir, orientation], '/')):
orientation = 'unflip'
return orientation
[docs]def skip_save(filenames: List[str], image_dir: str) -> bool:
"""Returns the flag on whether to skip saving a file.
If the path is not the intended path where the file will be saved, it will
skip saving the file. This makes it so the files are stored where they are intended
for use in reconstruction. The user may move them after they are saved at their
own precaution.
Args:
filenames: The list of filenames to save.
image_dir: The path of the current working directory for images.
Returns:
The flag for whether to save the files.
"""
skip_save_flag = False
for filename in filenames:
string = filename
index = 0
while index != -1:
last_index = index
index = string.rfind('/')
string = string[index + 1:]
folder = filename[:last_index]
if folder != image_dir:
skip_save_flag = True
return skip_save_flag
[docs]def file_loading(winfo: Struct, window: sg.Window, filename: str, active_key: str,
image_key: str, target_key: str, conflict_keys: List[str],
num_files: int, disable_elem_list: List[str]) -> Tuple[bool, List[str]]:
"""
The function for loading stacks and other image files.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
filename: The name of the file being loaded.
active_key: The key of the element that is active process.
image_key: The key of the image in the image dictionary file should be
loaded to.
target_key: The target key that should be updated when image is loaded.
conflict_keys: All the keys that should be disabled if they are in
conflict with the active key.
num_files: The number of files that should be loaded, 1 if not stack.
disable_elem_list: The list of elements to disable based off active key
or next active keys.
Returns:
remove: Boolean value if active key should be removed
disable_elem_list: The list of elements to disable based off active key
or next active keys.
"""
remove = False
# Path exists
if active_key.startswith('__REC'):
prefix = 'REC: '
if os_path.exists(filename):
with warnings.catch_warnings():
try:
# Is file loading correctly?
warnings.filterwarnings('error')
# Load images and convert to uint8 using numpy and hyperspy,
if active_key.startswith('__REC'):
graph_size_key, reset_key, fls_reset_key = ('__REC_Graph__', '__REC_Set_Img_Dir__', False)
graph_size = window[graph_size_key].get_size()
uint8_data, flt_data, size = util.load_image(filename, graph_size, active_key,
stack=True, prefix=prefix)
reset = (window[reset_key].metadata['State'] == 'Def' or
(not fls_reset_key or
window[fls_reset_key].metadata['State'] == 'Def'))
# Check if data was successfully converted to uint8
# Save the stack in the correct image dictionary
if (uint8_data and (num_files is None or num_files == len(uint8_data.keys()))
and not reset):
stack = util.Stack(uint8_data, flt_data, size, filename)
if active_key.startswith('__REC'):
winfo.rec_images[image_key] = stack
metadata_change(winfo, window, [(target_key, stack.shortname)])
toggle(winfo, window, [target_key], state="Set")
# Show which stacks/images are loaded
print(f'{prefix}The file {stack.shortname} was loaded.')
else:
# Incorrect file loaded, don't keep iterating through it
print(f'{prefix}An incorrect file was loaded. Either there was a file type error', end=' ')
print('or, if a stack, the number of files may not equal that expected from the FLS.')
remove = True
except ValueError:
print(f'{prefix}Value Error, had to remove item from queue.')
remove = True
# This warning captures the case when a file might be present after
# creation but hasn't fully loaded
except UserWarning:
disable_elem_list = disable_elem_list + conflict_keys
# Path doesn't exist, remove item from queue
else:
if len(filename) != 0:
print(f'{prefix}There is no valid image name here.')
remove = True
return remove, disable_elem_list
[docs]def readlines(process: 'subprocess.Popen', queue: 'queue.Queue') -> None:
"""Reads output that is passed to the queue from the running process.
Args:
process: The running process.
queue: The queue of the output stream.
Returns:
None
"""
while process.poll() is None:
queue.put(process.stdout.readline())
# ------------- Changing Element Values ------------- #
[docs]def update_values(winfo: Struct, window: sg.Window,
elem_val_list: List[Tuple[sg.Element, Any]]) -> None:
""" Take a list of element key, value tuple pairs
and update value of the element.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
elem_val_list: The list of elements, value paris to update.
Returns:
None
"""
for elem_key, value in elem_val_list:
if elem_key in winfo.keys['button']:
window[elem_key].Update(text=value)
else:
window[elem_key].Update(value=value)
[docs]def change_list_ind_color(window: sg.Window, current_tab: str,
elem_ind_val_list: List[int]) -> None:
"""Change the listbox index color based off what images are loaded.
Args:
window: The element representing the main GUI window.
current_tab: The key for the current tab.
elem_ind_val_list: The list of tuples made of PySimpleGUI elements
along with the value that the metadata of the
element state 'Set' will change to.
Returns:
None
"""
if current_tab == 'reconstruct_tab':
num_list = list(range(11))
for listbox_key, green_choices in elem_ind_val_list:
listbox = window[listbox_key]
grey_choices = setdiff1d(num_list, green_choices)
for index in green_choices:
listbox.Widget.itemconfig(index, fg='black', bg="light green")
for index in grey_choices:
listbox.Widget.itemconfig(index, fg='light grey', bg=sg.theme_input_background_color())
[docs]def change_inp_readonly_bg_color(window: sg.Window, elem_list: List[sg.Element],
val: str):
"""Change the readonly input background color.
Args:
window: The element representing the main GUI window.
elem_list: The list of elements whose color will change
val: The value to change the color to.
Returns:
None
"""
for elem in elem_list:
if val == 'Default':
window[elem].Widget.config(readonlybackground=sg.theme_input_background_color())
elif val == 'Readonly':
window[elem].Widget.config(readonlybackground='#A7A7A7')
[docs]def toggle(winfo: Struct, window: sg.Window,
elem_list: List[sg.Element], state: Optional[str] = None) -> None:
"""Toggle between the default state and set state
of an elements metadata.
Parameters:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
elem_list: The list of elements whose state is to be changed.
state: If the state is None, the state is changed
from Set -> Def or Def -> Set.
If the state is specified, that state will
be activated.
Returns:
None
"""
for elem in elem_list:
if state == 'Def':
new_state = window[elem].metadata['State'] = 'Def'
elif state == 'Set':
new_state = window[elem].metadata['State'] = 'Set'
else:
state = window[elem].metadata['State']
if state == 'Def':
new_state = 'Set'
elif state == 'Set':
new_state = 'Def'
window[elem].metadata['State'] = new_state
if new_state in window[elem].metadata:
value = window[elem].metadata[new_state]
update_values(winfo, window, [(elem, value)])
[docs]def update_slider(winfo: Struct, window: sg.Window,
slider_list: List[Tuple[sg.Element, Dict]]) -> None:
""" Updates sliders.
Updates sliders based off passing a list
with element, dictionary pairs. The dictionary
contains all values to update.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
new_transform: The next mask size to apply for REC graph.
slider_list : List of slider, dictionary tuple pairs where the dictionary
contains the values to update.
Returns:
None
"""
for slider_key, d in slider_list:
slider = window[slider_key]
for key in d:
if key == "value":
update_values(winfo, window, [(slider_key, d[key])])
elif key == "slider_range":
slider_range = d[key]
slider.metadata["slider_range"] = slider_range
window[slider_key].Update(range=slider_range)
[docs]def update_rotxy(winfo: Struct, window: sg.Window,
current_tab: str,
new_transform: Tuple[Union[int, float], Union[int, float], Union[int, float], bool]) -> (
Tuple[Union[int, float], Union[int, float], Union[int, float], bool]):
"""Update the rotation, x-trans, y-trans, and
flip coordinates for the transform to apply to
series of images.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
current_tab: The key representing the current main tab of the
window.
new_transform: The next transformation to potentially apply to
img.
Returns:
transform: The transformation to apply to the img.
"""
rot_val, x_val, y_val, h_flip = new_transform
transform = float(rot_val), float(x_val), float(y_val), h_flip
if current_tab == "reconstruct_tab":
pref = 'REC'
winfo.rec_transform = transform
rot_key, x_key, y_key = (f'__{pref}_transform_rot__',
f'__{pref}_transform_x__',
f'__{pref}_transform_y__')
elem_val_list = [(rot_key, str(rot_val)), (x_key, str(x_val)), (y_key, str(y_val))]
update_values(winfo, window, elem_val_list)
return transform
[docs]def update_mask_size(winfo: Struct, window: sg.Window,
new_transform: Tuple[Union[int, float]]) -> Tuple[float]:
"""Update the mask size.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
new_transform: The next mask size to apply for REC graph.
Returns:
mask_transform: The float of the mask size.
"""
# Make sure the mask is a tuple so it works with retrieve_transform()
mask_size = new_transform[0]
mask_transform = (float(mask_size), )
winfo.rec_mask = mask_transform
mask_size_key = '__REC_Mask_Size__'
elem_val_list = [(mask_size_key, str(mask_size))]
update_values(winfo, window, elem_val_list)
return mask_transform
[docs]def set_crop_data(winfo: Struct, graph: sg.Graph, images: Dict, ptie: TIE_params) -> None:
"""Set the ptie crop data for reconstruction.
Args:
winfo: The data structure holding all information about
windows and GUI.
graph: The graph of the reconstruction tab.
images: The dictionary of the loaded reconstruction images.
ptie: The TIE_params object for reconstruction.
Returns:
mask_transform: The float of the mask size.
"""
# Set crop data
bottom, top, left, right = None, None, None, None
for i in range(len(winfo.rec_mask_coords)):
x, y = winfo.rec_mask_coords[i]
if right is None or x > right:
right = x
if left is None or x < left:
left = x
if bottom is None or graph.get_size()[1] - y > bottom:
bottom = graph.get_size()[1] - y
if top is None or graph.get_size()[1] - y < top:
top = graph.get_size()[1] - y
if (bottom, top, left, right) == (None, None, None, None):
bottom, top, left, right = graph.get_size()[1], 0, 0, graph.get_size()[0]
# Scaling the image from the graph region to the regular sized image
reg_width, reg_height = images['REC_Stack'].lat_dims
scale_x, scale_y = reg_width / graph.get_size()[0], reg_height / graph.get_size()[1]
# Take care of odd number of pixels or if hitting boundaries
if round(right * scale_x) - round(left * scale_x) != round(bottom * scale_y) - round(top * scale_y):
if round(right * scale_x) - round(left * scale_x) < round(bottom * scale_y) - round(top * scale_y):
if (round(right * scale_x) - round(left * scale_x)) % 2 != 0:
if right == reg_width:
left -= 1
else:
right += 1
elif (round(bottom * scale_y) - round(top * scale_y)) % 2 != 0:
bottom -= 1
if round(right * scale_x) - round(left * scale_x) > round(bottom * scale_y) - round(top * scale_y):
if (round(right * scale_x) - round(left * scale_x)) % 2 != 0:
right -= 1
if right == reg_width:
left -= 1
else:
right += 1
elif (round(bottom * scale_y) - round(top * scale_y)) % 2 != 0:
if bottom == reg_height:
top -= 1
else:
bottom += 1
# Make sure lengths of sides even boundaries and set scaled indices
scaled_left, scaled_right = round(left * scale_x), round(right * scale_x)
scaled_bottom, scaled_top = round(bottom * scale_y), round(top * scale_y)
if scaled_left % 2 != 0:
scaled_left = max(scaled_left - 1, 0)
if scaled_right % 2 != 0:
scaled_right = min(scaled_right + 1, reg_width)
if scaled_top % 2 != 0:
scaled_top = max(scaled_top - 1, 0)
if scaled_bottom % 2 != 0:
scaled_bottom = min(scaled_bottom + 1, reg_height)
# Set ptie crop
ptie.crop['right'], ptie.crop['left'] = scaled_right, scaled_left
ptie.crop['bottom'], ptie.crop['top'] = scaled_bottom, scaled_top
winfo.graph_slice = (scaled_bottom - scaled_top, scaled_right - scaled_left) # y, x, z
winfo.rec_pad_info = (None, 0, 0, 0)
[docs]def erase_mask_data(winfo: Struct, graph: sg.Graph, current_tab: str, display_img: bytes,
values: dict) -> Tuple[bool, bool, bytes, Tuple, Tuple]:
"""Erase the masks on the region selection planes
Args:
winfo: The data structure holding all information about
windows and GUI.
graph: The graph of the reconstruction tab.
current_tab: The current selected tab in the GUI.
display_img: The current display image.
values: Dictionary of GUI window values associated with element keys.
Returns:
Tuple that contains booleans of whether to draw/adjust mask, along with transformation
and resulting image.
"""
util.erase_marks(winfo, graph, current_tab, full_erase=True)
graph_size = graph.get_size()
draw_mask = True
adjust = True
if winfo.window['__REC_Mask__'].metadata['State'] == 'Def':
winfo.rec_mask_coords = []
winfo.rec_mask_markers = []
draw_mask = False
adjust = False
stack = winfo.rec_images['REC_Stack']
slider_val = int(values["__REC_Slider__"])
transform = (0, 0, 0, False)
resized_mask = util.array_resize(winfo.rec_ptie.mask, winfo.window['__REC_Graph__'].get_size())
for i in range(stack.z_size):
stack.uint8_data[i] = np.multiply(stack.uint8_data[i], resized_mask)
stack.flt_data[i] = np.multiply(stack.flt_data[i], resized_mask)
stack.byte_data[i], stack.rgba_data[i] = util.adjust_image(stack.flt_data[i], transform, stack.x_size,
winfo.window['__REC_Graph__'].get_size()[
0])
image_choice = winfo.window['__REC_Image_List__'].get()[0]
if image_choice == 'Stack':
display_img = util.convert_to_bytes(stack.rgba_data[slider_val])
winfo.rec_corner1 = None
winfo.rec_corner2 = None
winfo.new_selection = True
winfo.rec_pad_info = (None, 0, 0, 0)
winfo.rec_mask_center = (graph_size[0] / 2, graph_size[1] / 2)
winfo.rec_mask = (50,)
mask_transform = (50,)
transform = (0, 0, 0, False)
update_values(winfo, winfo.window, [('__REC_transform_x__', '0'), ('__REC_transform_y__', '0'),
('__REC_transform_rot__', "0"), ('__REC_Mask_Size__', '50')])
return draw_mask, adjust, display_img, mask_transform, transform
# ------------- Visualizing Elements ------------- #
[docs]def set_pretty_focus(winfo: Struct, window: sg.Window, event: str) -> None:
""" Sets the focus to reduce unwanted placements of
cursor or focus within the GUI.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
event: The key for the values dictionary that represents
an event in the window.
Returns:
None
"""
# Set the 'true element' to be the one the cursor is hovering over.
if "+HOVER+" in event and not winfo.true_element:
winfo.true_element = event.replace("+HOVER+", "")
elif "+STOP_HOVER+" in event:
winfo.true_element = None
# Window click will never set focus on button
elif event == "Window Click":
if winfo.true_element is not None and winfo.true_element not in winfo.keys['button']:
window[winfo.true_element].SetFocus()
else:
winfo.invis_graph.SetFocus(force=True)
# Set pretty focus for log.
if event == 'Log Click':
winfo.output_invis_graph.SetFocus(force=True)
[docs]def rec_get_listbox_ind_from_key(key_list: List[str]) -> List[int]:
"""Get the listbox indices from key list to color once images have loaded.
Args:
key_list: The list of keys of items in the rec listbox.
Returns:
indices: The indices of the keys that were in the key_list.
"""
indices = [0, 2, 11]
for key in key_list:
if key == 'color_b':
ind = 1
elif key == 'bxt':
ind = 3
elif key == 'byt':
ind = 4
elif key == 'bbt':
ind = 5
elif key == 'phase_e':
ind = 6
elif key == 'phase_m':
ind = 7
elif key == 'dIdZ_e':
ind = 8
elif key == 'dIdZ_m':
ind = 9
elif key == 'inf_im':
ind = 10
indices.append(ind)
return indices
[docs]def activate_spinner(window: sg.Window, elem: sg.Element) -> None:
"""Activate loading spinner.
Args:
window: The element representing the passed GUI window.
elem: The element who's spinner should be disabled
Returns:
None
"""
spinner_fn = window[elem].metadata['Set']
window[elem].metadata['State'] = 'Set'
window[elem].Update(spinner_fn)
[docs]def deactivate_spinner(window: sg.Window, elem: sg.Element) -> None:
"""Deactivate loading spinner.
Args:
window: The element representing the passed GUI window.
elem: The element who's spinner should be disabled
Returns:
None
"""
background = window[elem].metadata['Def']
window[elem].metadata['State'] = 'Def'
window[elem].Update(filename=background)
[docs]def redraw_graph(graph: sg.Graph, display_image: Optional[bytes]) -> None:
"""Redraw graph.
Args:
graph: The graph element in the window.
display_image : If None, the graph is erased
Else, bytes representation of the image.
Returns:
None
"""
graph.Erase()
if display_image:
x, y = graph.get_size()
graph.DrawImage(data=display_image, location=(0, y-1))
[docs]def change_visibility(window: sg.Window, elem_val_list: List[Tuple[sg.Element, Any]]) -> None:
""" Take a list of element keys and change
visibility of the element.
Args:
window : The element representing the main GUI window.
elem_val_list : The list of elements with values whose
state is to be changed.
Returns:
None
"""
for elem_key, val in elem_val_list:
window[elem_key].Update(visible=val)
[docs]def disable_elements(window: sg.Window, elem_list: List[sg.Window]) -> None:
""" Take a list of element keys and disable the element.
Args:
window: The element representing the passed GUI window.
elem_list: The list of elements whose state is to be changed.
Returns:
None
"""
for elem_key in elem_list:
window[elem_key].Update(disabled=True)
[docs]def enable_elements(winfo: Struct, window: sg.Window, elem_list: List[sg.Window]) -> None:
""" Take a list of element keys and enable the element.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the passed GUI window.
elem_list: The list of elements whose state is to be changed.
Returns:
None
"""
for elem_key in elem_list:
if elem_key in winfo.keys['combo']:
window[elem_key].Update(readonly=True)
else:
window[elem_key].Update(disabled=False)
[docs]def ptie_init_thread(winfo: Struct, path: str, fls1_path: str, fls2_path: str,
stack_name: str, files1: List[str], files2: List[str],
single: bool, tfs_value: str) -> None:
""" Create the PYTIE initialization thread.
Function initializes the parameters for PYTIE. See load_data from
TIE_helper for more information on initialized parameters.
Args:
winfo: The data structure holding all information about
windows and GUI.
path: The path to datafolder.
fls1_path: Path to the first .fls file.
fls2_path: Path to the second .fls file.
stack_name: Name of the stack to perform reconstruction on.
files1: The files in the unflip or tfs folder.
files2: The files in the flip folder or None.
single: Boolean value if a single tfs series or flipped tfs.
tfs_value: The defined through focal series value by the user,
whether it is a single series or unflip/flip series.
Returns:
None
"""
try:
# Make sure there is not error with the accelerationg voltage val.
assert (float(winfo.window['__REC_M_Volt__'].get()) > 0)
accel_volt = float(winfo.window['__REC_M_Volt__'].get()) * 1e3
# Load in stack data and ptie data, pulling image filenames
string = stack_name
index = 0
# Make sure the stack is directly in the current working image directory.
while index != -1:
last_index = index
index = string.rfind('/')
string = string[index + 1:]
folder = stack_name[:last_index]
assert(folder == path[:-1])
stack1, stack2, ptie = load_data_GUI(path, fls1_path, fls2_path, stack_name, single)
string_vals = []
for def_val in ptie.defvals:
val = str(def_val)
string_vals.append(val)
# If single reconstruction, look for 'tfs' dir first and then try using 'unflip'
if tfs_value == 'Single':
prefix = 'tfs'
path1 = util.join([path, prefix], '/')
if not os.path.exists(path1):
prefix = 'unflip'
else:
prefix = 'unflip'
im_name = files1[0]
# Apply ptie_mask to stack
stack = winfo.rec_images['REC_Stack']
transform = (0, 0, 0, False)
resized_mask = util.array_resize(ptie.mask, winfo.window['__REC_Graph__'].get_size())
for i in range(stack.z_size):
stack.uint8_data[i] = np.multiply(stack.uint8_data[i], resized_mask)
stack.flt_data[i] = np.multiply(stack.flt_data[i], resized_mask)
stack.byte_data[i], stack.rgba_data[i] = util.adjust_image(stack.flt_data[i], transform, stack.x_size,
winfo.window['__REC_Graph__'].get_size()[0])
# Change the appearance and values in the GUI
metadata_change(winfo, winfo.window, [('__REC_Image__', f'{prefix}/{im_name}')])
length_slider = len(string_vals)
winfo.window['__REC_Def_Combo__'].update(value=string_vals[0], values=string_vals)
winfo.window['__REC_Def_List__'].update(ptie.defvals, set_to_index=0, scroll_to_index=0)
winfo.window['__REC_Def_List__'].metadata['length'] = length_slider
toggle(winfo, winfo.window, elem_list=['__REC_Set_FLS__'])
update_slider(winfo, winfo.window, [('__REC_Defocus_Slider__', {"slider_range": (0, max(length_slider - 3, 0)),
"value": 0})])
update_slider(winfo, winfo.window, [('__REC_Slider__', {"value": 0, "slider_range": (0, stack.z_size-1)})])
enable_elements(winfo, winfo.window, ['__REC_Def_Combo__', '__REC_QC_Input__',
'__REC_Mask__', "__REC_Erase_Mask__", '__REC_Run_TIE__',
"__REC_Slider__", "__REC_Colorwheel__", "__REC_Derivative__"])
disable_elements(winfo.window, ['__REC_Stack__', '__REC_FLS1__', '__REC_FLS2__', '__REC_M_Volt__'])
change_inp_readonly_bg_color(winfo.window, ['__REC_Stack__', '__REC_FLS1__', '__REC_FLS2__',
'__REC_M_Volt__'], 'Readonly')
change_inp_readonly_bg_color(winfo.window, ['__REC_QC_Input__'], 'Default')
values = winfo.window['__REC_Image_List__'].GetListValues()
index = winfo.window['__REC_Image_List__'].GetIndexes()
selected = values[index[0]]
if selected == 'Stack':
redraw_graph(winfo.window['__REC_Graph__'], stack.byte_data[0])
# Load all relevant PTIE data into winfo
winfo.rec_defocus_slider_set = 0
winfo.rec_ptie = ptie
winfo.rec_microscope = Microscope(E=accel_volt, Cs=200.0e3, theta_c=0.01e-3, def_spr=80.0)
winfo.rec_files1 = files1
winfo.rec_files2 = files2
except:
print(f'REC: Something went wrong during initialization.')
print(f'REC: 1. Check to make sure aligned file is in cwd and not somewhere else.')
print(f'REC: 2. Check to make sure the fls file(s) match the aligned file chosen.', end=' ')
print('Otherwise PYTIE will search the wrong directories.')
print(f'REC: 3. Check to see voltage is numerical and above 0.')
raise
enable_elements(winfo, winfo.window, ["__REC_Reset_FLS__", '__REC_Reset_Img_Dir__'])
winfo.ptie_init_thread = None
print('--- Exited PTIE Initialization ---')
[docs]def ptie_recon_thread(winfo: Struct, window: sg.Window, graph: sg.Graph,
colorwheel_graph: sg.Graph, images: Dict,
current_tab: str) -> None:
""" Create the PYTIE reconstruction thread.
Function initializes the thread that runs the PYTIE reconstruction. For more
information on the reconstruction, see TIE_reconstruct.py.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
graph: The reconstruction graph canvas.
colorwheel_graph: The graph in the window where to place the colorwheel.
images: The dictionary of images and their values.
current_tab: The key representing the current main tab of the
window.'
Returns:
None
"""
ptie = winfo.rec_ptie
microscope = winfo.rec_microscope
def_val = float(window['__REC_Def_Combo__'].Get())
def_ind = ptie.defvals.index(def_val)
dataname = 'example'
hsv = window['__REC_Colorwheel__'].get() == 'HSV'
save = False
sym = window['__REC_Symmetrize__'].Get()
qc = window['__REC_QC_Input__'].Get()
qc_passed = True
if util.represents_float(qc):
qc = float(qc)
if qc < 0:
qc_passed = False
elif qc == 0:
qc = None
else:
qc_passed = False
# Longitudinal deriv
deriv_val = window['__REC_Derivative__'].get()
if deriv_val == 'Longitudinal Deriv.':
longitudinal_deriv = True
elif deriv_val == 'Central Diff.':
longitudinal_deriv = False
# Set crop data
set_crop_data(winfo, graph, images, ptie)
if not qc_passed:
print(f'REC: QC value should be an integer or float and not negative. Change value.')
update_values(winfo, window, [('__REC_QC_Input__', '0.00')])
else:
try:
print(f'REC: Reconstructing for defocus value: {ptie.defvals[def_ind]} nm')
rot, x_trans, y_trans = (winfo.rec_transform[0], winfo.rec_transform[1], winfo.rec_transform[2])
ptie.rotation, ptie.x_transl, ptie.y_transl = float(rot), int(x_trans), int(y_trans)
results = TIE(def_ind, ptie, microscope, dataname, sym, qc, save, hsv, longitudinal_deriv, v=0)
winfo.rec_tie_results = results
winfo.rec_def_val = def_val
winfo.rec_sym = sym
winfo.rec_qc = qc
# Load the color image immediately after reconstruction
loaded_green_list = []
for key in results:
float_array = results[key]
# If single stack, just gloss over results that might be None.
if float_array is None:
continue
if key == 'color_b':
float_array = util.slice_im(float_array, (0, 0, winfo.graph_slice[0], winfo.graph_slice[1]))
colorwheel_type = window['__REC_Colorwheel__'].get()
rad1, rad2 = colorwheel_graph.get_size()
if colorwheel_type == 'HSV':
cwheel_hsv = colorwheel_HSV(rad1, background='black')
cwheel = colors.hsv_to_rgb(cwheel_hsv)
elif colorwheel_type == '4-Fold':
cwheel = colorwheel_RGB(rad1)
uint8_colorwheel, float_colorwheel = util.convert_float_unint8(cwheel, (rad1, rad2))
rgba_colorwheel = util.make_rgba(uint8_colorwheel[0])
winfo.rec_colorwheel = util.convert_to_bytes(rgba_colorwheel)
if float_array.shape[0] != float_array.shape[1]:
if float_array.shape[0] > float_array.shape[1]:
pad_side = (float_array.shape[0] - float_array.shape[1]) // 2
axis = 1
try:
if float_array.shape[2] > 0:
npad = ((0, 0), (int(pad_side), int(pad_side)), (0, 0))
except:
npad = ((0, 0), (int(pad_side), int(pad_side)))
elif float_array.shape[0] < float_array.shape[1]:
pad_side = (float_array.shape[1] - float_array.shape[0]) // 2
axis = 0
try:
if float_array.shape[2] > 0:
npad = ((int(pad_side), int(pad_side)), (0, 0), (0, 0))
except:
npad = ((int(pad_side), int(pad_side)), (0, 0))
float_array = np.pad(float_array, pad_width=npad, mode='constant', constant_values=0)
winfo.rec_pad_info = (axis, pad_side, float_array.shape[0], float_array.shape[1])
uint8_data, float_data = {}, {}
uint8_data, float_data = util.convert_float_unint8(float_array, graph.get_size(),
uint8_data, float_data)
if uint8_data:
image = util.FileImage(uint8_data, float_data, (winfo.graph_slice[0], winfo.graph_slice[1], 1), f'/{key}',
float_array=float_array)
image.byte_data = util.vis_1_im(image)
winfo.rec_images[key] = image
loaded_green_list.append(key)
else:
winfo.rec_images[key] = None
# Update window
list_color_indices = rec_get_listbox_ind_from_key(loaded_green_list)
change_list_ind_color(window, current_tab, [('__REC_Image_List__', list_color_indices)])
redraw_graph(graph, winfo.rec_images['color_b'].byte_data)
redraw_graph(colorwheel_graph, winfo.rec_colorwheel)
metadata_change(winfo, window, [('__REC_Image__', f'{def_val} Color')])
toggle(winfo, window, ['__REC_Image__'], state='Set')
update_slider(winfo, window, [('__REC_Image_Slider__', {"value": 7 - 1})])
window['__REC_Image_List__'].update(set_to_index=1, scroll_to_index=1)
change_inp_readonly_bg_color(window, ['__REC_QC_Input__'], 'Default')
winfo.rec_image_slider_set = 7 - 1
winfo.rec_last_image_choice = 'Color'
winfo.rec_ptie = ptie
except:
print(f'REC: There was an error when running TIE.')
raise
winfo.ptie_recon_thread = None
print('--- Exited Reconstruction ---')
[docs]def ptie_save(winfo: Struct, window: sg.Window, cwd: str, images: Dict,
filenames: List[str], pref: str, im_dir: str,
save_tie: Union[str, bool],
) -> None:
""" Save the current images of PYTIE.
Function saves the images of PYTIE, see TIE_reconstruct.py for more info.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
cwd: The current working directory that stores images and stacks.
images: The dictionary of images and their values.
filenames: List of filenames that will be saved.
pref: The string denoting the prefix name for labeling the images.
im_dir: The 'image directory for saving.
save_tie: The value for which save to apply to the current PYTIE images.
Can be a full save, color image save, or saving of x/y magnetizations
along with color.
Returns:
None
"""
# Check to see where the results should be saved
if not os_path.exists(f'{cwd}/images'):
os.mkdir(f'{cwd}/images')
winfo.rec_tie_prefix = pref
if save_tie != 'manual':
names = None
else:
names = filenames
save_results(winfo.rec_def_val, winfo.rec_tie_results, winfo.rec_ptie,
pref, winfo.rec_sym, winfo.rec_qc, save=save_tie, v=2,
directory=im_dir, long_deriv=False, filenames=names)
try:
if save_tie in [True, 'b'] or save_tie == 'manual':
if save_tie == 'manual':
arrow_filenames = []
for name in filenames:
if 'arrow' in name:
arrow_filenames.append(name)
else:
arrow_filenames = filenames[-2:]
if arrow_filenames:
hsv = window['__REC_Colorwheel__'].get() == "HSV"
color_float_array = images['color_b'].float_array
mag_x, mag_y = images['bxt'].float_array, images['byt'].float_array
v_num, v_len, v_wid = winfo.rec_past_arrow_transform[:3]
graph_size = window['__REC_Graph__'].get_size()
for i in range(len(arrow_filenames)):
name = arrow_filenames[i]
if i == 0:
v_color = True
else:
v_color = False
if winfo.rec_pad_info[0] is not None:
max_val = max(winfo.rec_pad_info[2:])
pad_side = winfo.rec_pad_info[1]
if winfo.rec_pad_info[0] == 1:
start_x, end_x = pad_side, max_val - pad_side
start_y, end_y = 0, max_val
elif winfo.rec_pad_info[0] == 0:
start_x, end_x = 0, max_val
start_y, end_y = pad_side, max_val - pad_side
color_float_array = util.slice_im(color_float_array, (start_y, start_x, end_y, end_x))
mag_x = util.slice_im(mag_x, (start_y, start_x, end_y, end_x))
mag_y = util.slice_im(mag_y, (start_y, start_x, end_y, end_x))
util.add_vectors(mag_x, mag_y, color_float_array, v_color, hsv, v_num, v_len,
v_wid, graph_size, winfo.rec_pad_info, save=name)
except:
print('Did not save images correctly')
raise
winfo.ptie_recon_thread = None
print('--- Exited Saving ---')
# -------------- Home Tab Event Handler -------------- #
[docs]def run_home_tab(winfo: Struct, window: sg.Window,
event: str, values: Dict) -> None:
"""Run events associated with the Home tab.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
event: The key for the values dictionary that represents
an event in the window.
values: A dictionary where every value is paired with
a key represented by an event in the window.
Returns:
None
"""
prefix = 'HOM: '
# Get directories for Fiji and image directory
python_dir = os.path.dirname(os.path.abspath(__file__))
# chmod
default_txt = f'{python_dir}/defaults.txt'
with open(default_txt, 'r') as f:
lines = f.readlines()
for line in lines:
if line.startswith('Browser Directory'):
items = line.split(',')
key, value = items[0], items[1]
value = value.strip()
if value:
color_def = window['__Browser_Set__'].metadata['Def']
color_res = window['__Browser_Reset__'].metadata['Set']
else:
color_def = window['__Browser_Set__'].metadata['Set']
color_res = window['__Browser_Reset__'].metadata['Def']
if winfo.last_browser_color != color_def:
window['__Browser_Set__'].update(button_color=color_def)
window['__Browser_Reset__'].update(button_color=color_res)
winfo.last_browser_color = color_def
if event == '__Browser_Set__':
python_dir = os.path.dirname(os.path.abspath(__file__))
default_txt = f'{python_dir}/defaults.txt'
with open(default_txt, 'r') as f:
lines = f.readlines()
with open(default_txt, 'w+') as fnew:
for line in lines:
if not line.startswith('//'):
items = line.split(',')
key, value = items[0], items[1]
if key == 'Browser Directory' and event == '__Browser_Set__':
filename = window["__Browser_Path__"].Get()
if os_path.exists(filename):
fnew.write(f'Browser Directory,{filename}\n')
print(f'{prefix}Browser working directory default was set.')
window['__REC_Image_Dir_Browse__'].InitialFolder = filename
else:
fnew.write(line)
print(f'{prefix}Directory does not exist, try again.')
elif key == 'Browser Directory' and event != '__Browser_Set__':
fnew.write(line)
else:
fnew.write(line)
elif line.startswith('//'):
fnew.write(line)
elif event in ['__Browser_Reset__']:
python_dir = os.path.dirname(os.path.abspath(__file__))
default_txt = f'{python_dir}/defaults.txt'
with open(default_txt, 'r') as f:
lines = f.readlines()
with open(default_txt, 'w') as fnew:
for line in lines:
if line.startswith('Browser Directory') and event == '__Browser_Reset__':
update_values(winfo, window, [('__Browser_Path__', '')])
fnew.write('Browser Directory, \n')
print(f'{prefix}Browser working directory default was reset.')
window['__REC_Image_Dir_Browse__'].InitialFolder = ''
elif line.startswith('FIJI Directory'):
fnew.write('FIJI Directory, \n')
else:
fnew.write(line)
# -------------- Reconstruct Tab Event Handler -------------- #
[docs]def run_reconstruct_tab(winfo: Struct, window: sg.Window,
current_tab: str, event: str, values: Dict) -> None:
"""Run events associated with the reconstruct tab.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
current_tab: The key representing the current main tab of the
window. Ex. '
event: The key for the values dictionary that represents
an event in the window.
values: A dictionary where every value is paired with
a key represented by an event in the window.
Returns:
None
"""
# ------------- Visualizing Elements ------------- #
def special_enable_disable(winfo: Struct, window: sg.Window) -> None:
"""Determine enabling and disabling of elements based off loaded buttons and active processes.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
Returns:
None
"""
enable_list = []
active_keys = ['__REC_Image_Dir_Path__', '__REC_Set_Img_Dir__', '__REC_Image_Dir_Browse__',
'__REC_FLS_Combo__', '__REC_Load_FLS1__', '__REC_Set_FLS__',
'__REC_Load_FLS2__', '__REC_Load_Stack__', '__REC_Image_List__',
'__REC_M_Volt__', '__REC_Def_Combo__', '__REC_QC_Input__',
"__REC_Reset_FLS__", "__REC_TFS_Combo__", "__REC_Arrow_Num__",
'__REC_Arrow_Wid__', '__REC_Arrow_Len__', '__REC_Arrow_Color__',
'__REC_Mask_Size__', '__REC_Mask__', "__REC_Erase_Mask__",
"__REC_transform_y__", "__REC_transform_x__", "__REC_transform_rot__",
'__REC_Run_TIE__', '__REC_Save_TIE__', "__REC_Square_Region__", "__REC_Rectangle_Region__",
"__REC_Slider__", "__REC_Colorwheel__", "__REC_Derivative__",
'__REC_Reset_Img_Dir__', "__REC_Arrow_Set__"]
if window['__REC_Set_Img_Dir__'].metadata['State'] == 'Set':
if winfo.ptie_recon_thread is None and winfo.ptie_init_thread is None:
enable_list.extend(["__REC_Reset_FLS__"])
if window['__REC_Set_FLS__'].metadata['State'] == 'Def':
enable_list.extend(['__REC_FLS_Combo__', "__REC_TFS_Combo__", '__REC_M_Volt__'])
if (window['__REC_FLS_Combo__'].Get() == 'Two' and
window['__REC_FLS2__'].metadata['State'] == 'Def'):
enable_list.extend(['__REC_Load_FLS2__'])
if window['__REC_FLS1__'].metadata['State'] == 'Def':
enable_list.extend(['__REC_Load_FLS1__'])
if window['__REC_Stack__'].metadata['State'] == 'Def':
enable_list.append('__REC_Load_Stack__')
if (window['__REC_Stack__'].metadata['State'] == 'Set' and
window['__REC_FLS1__'].metadata['State'] == 'Set' and
window['__REC_FLS2__'].metadata['State'] == 'Set' and
winfo.ptie_init_thread is None):
enable_list.extend(['__REC_Set_FLS__'])
elif window['__REC_Set_FLS__'].metadata['State'] == 'Set' and winfo.ptie_recon_thread is None:
enable_list.extend(['__REC_Erase_Mask__',
'__REC_Def_Combo__', "__REC_Colorwheel__",
'__REC_QC_Input__', "__REC_Derivative__"])
if winfo.rec_rotxy_timers == (0, 0, 0) and winfo.rec_mask_timer == (0,):
enable_list.extend(['__REC_Mask__'])
if window['__REC_Mask__'].metadata['State'] == 'Def':
enable_list.extend(['__REC_Run_TIE__'])
enable_list.extend(["__REC_Arrow_Num__", '__REC_Arrow_Color__',
'__REC_Arrow_Wid__', '__REC_Arrow_Len__']),
if 'color_b' in winfo.rec_images:
enable_list.extend(["__REC_Arrow_Set__"])
else:
if window['__REC_Square_Region__'].Get():
enable_list.extend(['__REC_Mask_Size__'])
enable_list.extend(["__REC_transform_y__", "__REC_Square_Region__", "__REC_Rectangle_Region__",
"__REC_transform_x__", "__REC_transform_rot__"])
if winfo.rec_tie_results is not None and winfo.ptie_recon_thread is None:
enable_list.extend(['__REC_Save_TIE__'])
if (window['__REC_Stack__'].metadata['State'] == 'Set' and
window['__REC_Mask__'].metadata['State'] == 'Def'):
enable_list.extend(["__REC_Image_List__"])
if (window['__REC_Image_List__'].get()[0] in ['Stack', 'Loaded Stack'] or
window['__REC_Mask__'].metadata['State'] == 'Set'):
enable_list.extend(["__REC_Slider__"])
elif window['__REC_Set_Img_Dir__'].metadata['State'] == 'Def':
enable_list.extend(['__REC_Image_Dir_Path__', '__REC_Set_Img_Dir__',
'__REC_Image_Dir_Browse__'])
if winfo.ptie_recon_thread is None and winfo.ptie_init_thread is None:
enable_list.extend(['__REC_Reset_Img_Dir__'])
disable_list = setdiff1d(active_keys, enable_list)
if ((winfo.last_rec_disable is None or winfo.last_rec_enable is None) or
(collections.Counter(disable_list) != collections.Counter(winfo.last_rec_disable) and
collections.Counter(enable_list) != collections.Counter(winfo.last_rec_enable))):
disable_elements(window, disable_list)
enable_elements(winfo, window, enable_list)
winfo.last_rec_enable = enable_list
winfo.last_rec_disable = disable_list
# Get rotations and shifts to apply to image (only positive rotations)
transform = get_transformations(winfo, window, current_tab)
mask_transform = get_mask_transform(winfo, window, current_tab)
# Grab important elements
graph = window['__REC_Graph__']
colorwheel_graph = window['__REC_Colorwheel_Graph__']
mask_button = window['__REC_Mask__']
# Pull in image data from struct object
image_dir = winfo.rec_image_dir
images = winfo.rec_images
colorwheel_choice = window['__REC_Colorwheel__'].Get()[0]
if winfo.ptie_init_thread is not None and not winfo.ptie_init_thread.is_alive():
winfo.ptie_init_thread = None
if winfo.ptie_recon_thread is not None and not winfo.ptie_recon_thread.is_alive():
winfo.ptie_recon_thread = None
if winfo.ptie_recon_thread is not None:
winfo.rec_past_recon_thread = 'alive'
elif winfo.ptie_recon_thread is None and winfo.rec_past_recon_thread is not None:
if 'color_b' in images:
# Add the vector image
hsv = window['__REC_Colorwheel__'].get() == 'HSV'
color_float_array = images['color_b'].float_array
mag_x, mag_y = images['bxt'].float_array, images['byt'].float_array
vector_color = window['__REC_Arrow_Color__'].get()
if vector_color == 'On':
vector_color = True
elif vector_color == 'Off':
vector_color = False
vector_num = int(window['__REC_Arrow_Num__'].get())
vector_len, vector_wid = float(window['__REC_Arrow_Len__'].get()), float(window['__REC_Arrow_Wid__'].get())
graph_size = graph.get_size()
byte_img = util.add_vectors(mag_x, mag_y, color_float_array,
vector_color, hsv, vector_num, vector_len,
vector_wid, graph_size, winfo.rec_pad_info, save=None)
shape = color_float_array.shape
im = util.FileImage(np.empty(shape), np.empty(shape),
(winfo.graph_slice[0], winfo.graph_slice[1], 1), '/vector')
im.byte_data = byte_img
winfo.rec_images['vector'] = im
winfo.rec_past_recon_thread = None
prefix = 'REC: '
display_img = None
display_img2 = None
# Import event handler names (overlaying, etc.)
adjust = mask_button.metadata['State'] == 'Set' and (winfo.rec_past_transform != transform or
winfo.rec_past_mask != mask_transform)
image_list = window['__REC_Image_List__'].get()
try:
change_img = winfo.rec_last_image_choice != image_list[0]
except:
list_values = window['__REC_Image_List__'].GetListValues()
last_index = list_values.index(winfo.rec_last_image_choice)
window['__REC_Image_List__'].update(set_to_index=last_index)
change_img = True
change_colorwheel = winfo.rec_last_colorwheel_choice != colorwheel_choice
scroll = (event in ['MouseWheel:Up', 'MouseWheel:Down']
and (window['__REC_Image_List__'].get()[0] in ['Stack', 'Loaded Stack'] or
window['__REC_Mask__'].metadata['State'] == 'Set')
and winfo.rec_images
and winfo.true_element == "__REC_Graph__")
scroll_defocus = (event in event in ['MouseWheel:Up', 'MouseWheel:Down'] and
winfo.true_element == '__REC_Def_List__' or
event == '__REC_Defocus_Slider__')
scroll_images = (event in event in ['MouseWheel:Up', 'MouseWheel:Down'] and
winfo.true_element == '__REC_Image_List__' or
event == '__REC_Image_Slider__')
draw_mask = mask_button.metadata['State'] == 'Set'
# Set the working directory
if event == '__REC_Set_Img_Dir__':
image_dir = values['__REC_Image_Dir_Path__']
if os_path.exists(image_dir):
winfo.rec_image_dir = image_dir
toggle(winfo, window, ['__REC_Set_Img_Dir__'], state='Set')
change_inp_readonly_bg_color(window, ['__REC_Stack__', '__REC_FLS1__', '__REC_FLS2__'], 'Default')
print(f'{prefix}The path is set: {image_dir}.')
else:
print(f'{prefix}This pathname is incorrect.')
# Load Stack
elif event == '__REC_Stack_Stage__':
stack_path = window['__REC_Stack_Stage__'].Get()
update_values(winfo, window, [('__REC_Stack_Stage__', 'None')])
if os_path.exists(stack_path) and (stack_path.endswith('.tif') or stack_path.endswith('.tiff')):
graph = window['__REC_Graph__']
graph_size = graph.get_size()
uint8_data, flt_data, size = util.load_image(stack_path, graph_size, event, stack=True, prefix='REC: ')
if uint8_data:
stack = util.Stack(uint8_data, flt_data, size, stack_path)
stack_def = util.Stack(uint8_data, flt_data, size, stack_path)
slider_range = (0, stack.z_size - 1)
slider_val = 0
winfo.rec_images['REC_Stack'] = stack
winfo.rec_images['REC_Def_Stack'] = stack_def
for i in range(stack.z_size):
stack.byte_data[i], stack.rgba_data[i] = util.adjust_image(stack.flt_data[i], transform,
stack.x_size, graph.get_size()[0])
if i == slider_val:
display_img = stack.byte_data[i]
metadata_change(winfo, window, [('__REC_Stack__', stack.shortname)])
toggle(winfo, window, ['__REC_Stack__', '__REC_Image__'], state="Set")
update_slider(winfo, window, [('__REC_Slider__', {"value": slider_val, "slider_range": slider_range})])
winfo.rec_last_image_choice = 'Stack'
change_list_ind_color(window, current_tab, [('__REC_Image_List__', [0, 11])])
change_inp_readonly_bg_color(window, ['__REC_Stack__'], 'Readonly')
metadata_change(winfo, window, [('__REC_Image__', f'Image {slider_val + 1}')])
print(f'{prefix}The file {stack.shortname} was loaded.')
else:
if len(stack_path) != 0 and stack_path != "None":
print(f'{prefix}Stack path is not valid.')
# Set number of FLS files to use
elif event == '__REC_FLS_Combo__' or event == '__REC_TFS_Combo__':
fls_value = window['__REC_FLS_Combo__'].Get()
tfs_value = window['__REC_TFS_Combo__'].Get()
winfo.rec_fls_files = [None, None]
metadata_change(winfo, window, ['__REC_FLS2__', '__REC_FLS1__'], reset=True)
toggle(winfo, window, ['__REC_FLS2__', '__REC_FLS1__'], state='Def')
change_inp_readonly_bg_color(window, ['__REC_FLS1__', '__REC_FLS2__'], 'Default')
# FLS Combo Chosen
if event == '__REC_FLS_Combo__':
# If one fls file is to be used
metadata_change(winfo, window, [('__REC_FLS_Combo__', fls_value)])
if fls_value == 'One':
toggle(winfo, window, ['__REC_FLS_Combo__', '__REC_FLS2__'], state='Set')
if tfs_value == 'Unflip/Flip':
val = 'Both'
elif tfs_value == 'Single':
val = tfs_value
change_inp_readonly_bg_color(window, ['__REC_FLS2__'], 'Readonly')
# If two fls file is to be used
elif fls_value == 'Two':
val = fls_value
metadata_change(winfo, window, ['__REC_FLS_Combo__', '__REC_TFS_Combo__'], reset=True)
toggle(winfo, window, ['__REC_FLS_Combo__', '__REC_TFS_Combo__',
'__REC_FLS2__'], state='Def')
# TFS Combo Chosen
elif event == '__REC_TFS_Combo__':
metadata_change(winfo, window, [('__REC_TFS_Combo__', tfs_value)])
if tfs_value == 'Unflip/Flip':
val = 'Two'
metadata_change(winfo, window, ['__REC_FLS_Combo__', '__REC_TFS_Combo__'], reset=True)
toggle(winfo, window, ['__REC_FLS_Combo__', '__REC_TFS_Combo__',
'__REC_FLS2__'], state='Def')
elif tfs_value == 'Single':
val = tfs_value
metadata_change(winfo, window, [('__REC_FLS_Combo__', 'One')])
toggle(winfo, window, ['__REC_FLS_Combo__', '__REC_TFS_Combo__',
'__REC_FLS2__'], state='Set')
change_inp_readonly_bg_color(window, ['__REC_FLS2__'], 'Readonly')
window['__REC_FLS1_Text__'].update(value=window['__REC_FLS1_Text__'].metadata[val])
window['__REC_FLS2_Text__'].update(value=window['__REC_FLS2_Text__'].metadata[val])
# Load FLS files
elif event == '__REC_FLS1_Staging__' or event == '__REC_FLS2_Staging__':
tfs_value = window['__REC_TFS_Combo__'].Get()
fls_value = window['__REC_FLS_Combo__'].Get()
if 'FLS1' in event:
fls_path = window['__REC_FLS1_Staging__'].Get()
update_values(winfo, window, [('__REC_FLS1_Staging__', 'None')])
target_key = '__REC_FLS1__'
elif 'FLS2' in event:
fls_path = window['__REC_FLS2_Staging__'].Get()
update_values(winfo, window, [('__REC_FLS2_Staging__', 'None')])
target_key = '__REC_FLS2__'
if os_path.exists(fls_path) and fls_path.endswith('.fls'):
fls = util.FileObject(fls_path)
if 'FLS1' in event:
winfo.rec_fls_files[0] = fls
if tfs_value == 'Unflip/Flip' and fls_value == 'One':
winfo.rec_fls_files[1] = fls
elif 'FLS2' in event:
winfo.rec_fls_files[1] = fls
metadata_change(winfo, window, [(target_key, fls.shortname)])
toggle(winfo, window, [target_key], state='Set')
change_inp_readonly_bg_color(window, [target_key], 'Readonly')
else:
if len(fls_path) != 0 and fls_path != "None":
print(f'{prefix}File is not read as an fls file.')
# Set number of FLS files to use
elif event == '__REC_Reset_FLS__':
winfo.rec_images = {}
winfo.rec_fls_files = [None, None]
winfo.rec_ptie = None
# --- Set up loading files --- #
winfo.rec_defocus_slider_set = 0
winfo.rec_image_slider_set = 7
# Image selection
winfo.rec_tie_results = None
winfo.rec_def_val = None
# Graph and mask making
winfo.graph_slice = (None, None)
winfo.rec_graph_double_click = False
winfo.rec_mask_coords = []
winfo.rec_mask_markers = []
winfo.rec_pad_info = (None, 0, 0, 0)
graph.Erase()
colorwheel_graph.Erase()
metadata_change(winfo, window, ['__REC_FLS1__', '__REC_FLS2__', '__REC_Stack__'], reset=True)
toggle(winfo, window, ['__REC_FLS_Combo__', '__REC_TFS_Combo__', '__REC_Stack__',
'__REC_FLS1__', '__REC_FLS2__', '__REC_Set_FLS__', '__REC_Image__',
'__REC_Mask__'], state='Def')
window['__REC_Def_Combo__'].update(value='None', values=['None'])
window['__REC_Def_List__'].update(values=['None'])
window['__REC_FLS1_Text__'].update(value=window['__REC_FLS1_Text__'].metadata['Two'])
window['__REC_FLS2_Text__'].update(value=window['__REC_FLS2_Text__'].metadata['Two'])
window['__REC_FLS1_Text__'].metadata['State'] = 'Two'
window['__REC_FLS2_Text__'].metadata['State'] = 'Two'
update_values(winfo, window, [('__REC_FLS1_Staging__', ''), ('__REC_FLS2_Staging__', ''),
('__REC_Stack_Stage__', ''), ('__REC_Def_Combo__', 'None'),
('__REC_Image__', 'None')])
change_list_ind_color(window, current_tab, [('__REC_Image_List__', [])])
change_inp_readonly_bg_color(window, ['__REC_Stack__', '__REC_FLS1__',
'__REC_FLS2__'], 'Default')
change_inp_readonly_bg_color(window, ['__REC_QC_Input__'], 'Readonly')
# Re-init reconstruct
update_slider(winfo, window, [('__REC_Defocus_Slider__', {'value': winfo.rec_defocus_slider_set,
'slider_range': (0, 0)}),
('__REC_Slider__', {'value': 0, 'slider_range': (0, 0)}),
('__REC_Image_Slider__', {'value': winfo.rec_image_slider_set})])
window['__REC_Image_List__'].update(set_to_index=0, scroll_to_index=0)
window['__REC_Def_List__'].update(set_to_index=0, scroll_to_index=0)
print(f'{prefix}FLS and reconstruct data reset.')
# Set which image you will be working with FLS files
elif event == '__REC_Set_FLS__':
# Get PYTIE loading params
path = image_dir + '/'
stack_name = images['REC_Stack'].path
# Get FLS value information
tfs_value = window['__REC_TFS_Combo__'].Get()
fls_value = window['__REC_FLS_Combo__'].Get()
if tfs_value == 'Unflip/Flip':
fls_file_names = [winfo.rec_fls_files[0].path, winfo.rec_fls_files[1].path]
else:
fls_file_names = [winfo.rec_fls_files[0].path, None]
check = check_setup(image_dir, tfs_value, fls_value, fls_file_names, prefix='REC: ')
# The resulting check.
if check and check[1] is not None:
path1, path2, files1, files2 = check[1:]
fls_1 = winfo.rec_fls_files[0]
fls_2 = winfo.rec_fls_files[1]
fls1_path = fls_1.path
if tfs_value != 'Single':
fls2_path = fls_2.path
else:
fls2_path = None
# Is this single series or flipped/unflipped series
if tfs_value != 'Single':
single = False
else:
single = True
# Load ptie params
if ((2*len(files1) == images['REC_Stack'].z_size and tfs_value == 'Unflip/Flip') or
(len(files1) == images['REC_Stack'].z_size and tfs_value == 'Single')):
winfo.ptie_init_thread = Thread(target=ptie_init_thread,
args=(winfo, path, fls1_path, fls2_path, stack_name,
files1, files2, single, tfs_value),
daemon=True)
print('--- Start PTIE Initialization ---')
winfo.ptie_init_thread.start()
else:
print(f'{prefix}The number of expected files does not match the', end=' ')
print('current stack.')
else:
print(f'{prefix}There was an incompatibility between the fls contents and the', end=' ')
print('files within the directories. Check to make sure the folder you loaded', end=' ')
print('the image stack from is the set working directory.', end=' ')
# Change the slider
elif event == '__REC_Slider__':
stack_choice = window['__REC_Image_List__'].get()[0]
if stack_choice == 'Stack':
stack_key = 'REC_Stack'
elif stack_choice == 'Loaded Stack':
stack_key = 'REC_Def_Stack'
stack = images[stack_key]
slider_val = int(values["__REC_Slider__"])
# Update window
if stack_key == 'REC_Def_Stack':
display_img = stack.byte_data[slider_val]
elif stack_key == 'REC_Stack':
if window['__REC_Mask__'].metadata['State'] == 'Set':
display_img, stack.rgba_data[slider_val] = util.adjust_image(stack.flt_data[slider_val], transform,
stack.x_size, graph.get_size()[0])
else:
display_img = util.convert_to_bytes(stack.rgba_data[slider_val])
if winfo.rec_files1:
if winfo.rec_files1 and winfo.rec_files2:
if slider_val < len(winfo.rec_files1):
pref = 'unflip'
im_name = winfo.rec_files1[slider_val]
elif slider_val >= len(winfo.rec_files1):
pref = 'flip'
im_name = winfo.rec_files2[slider_val % len(winfo.rec_files1)]
else:
if os_path.exists(f'{image_dir}/tfs/'):
pref = 'tfs'
else:
pref = 'unflip'
im_name = winfo.rec_files1[slider_val]
metadata_change(winfo, window, [('__REC_Image__', f'{pref}/{im_name}')])
else:
metadata_change(winfo, window, [('__REC_Image__', f'Image {slider_val+1}')])
# Scroll through stacks in the graph area
elif scroll:
stack_choice = window['__REC_Image_List__'].get()[0]
if stack_choice in ['Stack'] or window['__REC_Mask__'].metadata['State'] == 'Set':
stack = images['REC_Stack']
elif stack_choice == 'Loaded Stack':
stack = images['REC_Def_Stack']
slider_val = int(values["__REC_Slider__"])
max_slider_val = stack.z_size - 1
# Scroll up or down
if event == 'MouseWheel:Down':
slider_val = min(max_slider_val, slider_val+1)
elif event == 'MouseWheel:Up':
slider_val = max(0, slider_val-1)
# Update the window
if stack_choice == 'Stack':
if window['__REC_Mask__'].metadata['State'] == 'Set':
display_img, stack.rgba_data[slider_val] = util.adjust_image(stack.flt_data[slider_val], transform,
stack.x_size, graph.get_size()[0])
else:
display_img = util.convert_to_bytes(stack.rgba_data[slider_val])
elif stack_choice == 'Loaded Stack':
display_img = stack.byte_data[slider_val]
update_slider(winfo, window, [('__REC_Slider__', {"value": slider_val})])
if winfo.rec_files1:
if winfo.rec_files1 and winfo.rec_files2:
if slider_val < len(winfo.rec_files1):
pref = 'unflip'
im_name = winfo.rec_files1[slider_val]
elif slider_val >= len(winfo.rec_files1):
pref = 'flip'
im_name = winfo.rec_files2[slider_val % len(winfo.rec_files1)]
else:
if os_path.exists(f'{image_dir}/tfs/'):
pref = 'tfs'
else:
pref = 'unflip'
im_name = winfo.rec_files1[slider_val]
metadata_change(winfo, window, [('__REC_Image__', f'{pref}/{im_name}')])
else:
metadata_change(winfo, window, [('__REC_Image__', f'Image {slider_val+1}')])
# Scroll through image options
elif scroll_images:
max_slider_val = 7
if event in ['MouseWheel:Down', 'MouseWheel:Up']:
slider_set = winfo.rec_image_slider_set
if event == 'MouseWheel:Up':
slider_val = min(max_slider_val, slider_set + 1)
elif event == 'MouseWheel:Down':
slider_val = max(0, slider_set - 1)
elif event == "__REC_Image_Slider__":
slider_val = int(values["__REC_Image_Slider__"])
update_slider(winfo, window, [('__REC_Image_Slider__', {"value": slider_val})])
window['__REC_Image_List__'].update(scroll_to_index=max_slider_val-slider_val)
winfo.rec_image_slider_set = slider_val
# Scroll through defocus options
elif scroll_defocus:
max_slider_val = max(window['__REC_Def_List__'].metadata['length'] - 3, 0)
if event in ['MouseWheel:Down', 'MouseWheel:Up']:
slider_set = winfo.rec_defocus_slider_set
if event == 'MouseWheel:Down':
slider_val = min(max_slider_val, slider_set + 1)
elif event == 'MouseWheel:Up':
slider_val = max(0, slider_set - 1)
elif event == "__REC_Defocus_Slider__":
slider_val = int(values["__REC_Defocus_Slider__"])
update_slider(winfo, window, [('__REC_Defocus_Slider__', {"value": slider_val})])
window['__REC_Def_List__'].update(scroll_to_index=slider_val)
winfo.rec_defocus_slider_set = slider_val
# Changing view stack combo
elif change_img:
list_values = window['__REC_Image_List__'].GetListValues()
last_index = None
if winfo.rec_last_image_choice is not None:
last_index = list_values.index(winfo.rec_last_image_choice)
image_choice = window['__REC_Image_List__'].get()[0]
if image_choice == 'Stack':
image_key = 'REC_Stack'
elif image_choice == 'Loaded Stack':
image_key = 'REC_Def_Stack'
elif image_choice == 'Color':
image_key = 'color_b'
im_name = 'Color'
elif image_choice == 'Vector Im.':
image_key = 'vector'
im_name = 'Vectorized Msat'
elif image_choice == 'MagX':
image_key = 'bxt'
im_name = 'X-Comp. of Mag. Induction'
elif image_choice == 'MagY':
image_key = 'byt'
im_name = 'Y-Comp. of Mag. Induction'
elif image_choice == 'Mag. Magnitude':
image_key = 'bbt'
im_name = 'Magnitude of Mag. Induction'
elif image_choice == 'Mag. Phase':
image_key = 'phase_m'
im_name = 'Magnetic Phase Shift (radians)'
elif image_choice == 'Electr. Phase':
image_key = 'phase_e'
im_name = 'Electrostatic Phase Shift (radians)'
elif image_choice == 'Mag. Deriv.':
image_key = 'dIdZ_m'
im_name = 'Intensity Deriv. for Mag. Phase'
elif image_choice == 'Electr. Deriv.':
image_key = 'dIdZ_e'
im_name = 'Intensity Deriv. for Electr. Phase'
elif image_choice == 'In Focus':
image_key = 'inf_im'
im_name = 'In-focus image'
if values['__REC_TFS_Combo__'][0] == 'Single' and image_key in ['dIdZ_e', 'phase_e']:
window['__REC_Image_List__'].update(set_to_index=last_index)
print(f'{prefix}Electric information not available for single TFS.')
else:
if image_key in images and image_choice in ['Stack', 'Loaded Stack'] and images[image_key] is not None:
stack = images[image_key]
slider_val = 0
slider_range = (0, stack.z_size - 1)
# Update window
if winfo.rec_files1:
if winfo.rec_files1 and winfo.rec_files2:
if slider_val < len(winfo.rec_files1):
pref = 'unflip'
im_name = winfo.rec_files1[slider_val]
elif slider_val >= len(winfo.rec_files1):
pref = 'flip'
im_name = winfo.rec_files2[slider_val % len(winfo.rec_files1)]
else:
if os_path.exists(f'{image_dir}/tfs/'):
pref = 'tfs'
else:
pref = 'unflip'
im_name = winfo.rec_files1[slider_val]
metadata_change(winfo, window, [('__REC_Image__', f'{pref}/{im_name}')])
else:
metadata_change(winfo, window, [('__REC_Image__', f'Image {slider_val + 1}')])
if image_key == 'REC_Stack':
display_img = util.convert_to_bytes(stack.rgba_data[slider_val])
elif image_key == 'REC_Def_Stack':
display_img = stack.byte_data[slider_val]
colorwheel_graph.Erase()
update_slider(winfo, window, [('__REC_Slider__', {"value": slider_val, "slider_range": slider_range})])
# Other image set
elif image_key in images and images[image_key] is not None:
image = images[image_key]
display_img = image.byte_data
if image_key == 'color_b' or (image_key == 'vector' and window['__REC_Arrow_Color__'].Get() == 'On'):
display_img2 = winfo.rec_colorwheel
else:
colorwheel_graph.Erase()
metadata_change(winfo, window, [('__REC_Image__', f'{winfo.rec_def_val} {im_name}')])
elif last_index is not None:
window['__REC_Image_List__'].update(set_to_index=last_index)
print(f"{prefix}Image is not available to view. Check PYTIE is run.")
if values['__REC_TFS_Combo__'] == 'Single':
print(f"{prefix}For a single TFS, electric deriv. and phase are not available.")
winfo.rec_last_image_choice = image_choice
# Start making reconstruct subregion
elif event == '__REC_Mask__':
stack = images['REC_Stack']
slider_range = (0, stack.z_size - 1)
slider_val = int(values["__REC_Slider__"])
if winfo.rec_files1:
if winfo.rec_files1 and winfo.rec_files2:
if slider_val < len(winfo.rec_files1):
prefix = 'unflip'
im_name = winfo.rec_files1[slider_val]
elif slider_val >= len(winfo.rec_files1):
prefix = 'flip'
im_name = winfo.rec_files2[slider_val % len(winfo.rec_files1)]
else:
prefix = 'tfs'
im_name = winfo.rec_files1[slider_val]
metadata_change(winfo, window, [('__REC_Image__', f'{prefix}/{im_name}')])
# Start mask making make_mask_button
if mask_button.metadata['State'] == 'Def':
toggle(winfo, window, ['__REC_Mask__'], state='Set')
update_slider(winfo, window, [('__REC_Slider__', {"value": slider_val, "slider_range": slider_range})])
draw_mask = True
display_img, rgba_img = util.adjust_image(stack.flt_data[slider_val],
transform, stack.x_size, graph.get_size()[0])
if window['__REC_Square_Region__'].Get():
util.draw_square_mask(winfo, graph)
# Quit mask making make_mask_button
elif mask_button.metadata['State'] == 'Set':
if winfo.rec_mask_coords and ((abs(winfo.rec_mask_coords[2][0] - winfo.rec_mask_coords[0][0]) > 16) and
(abs(winfo.rec_mask_coords[2][1] - winfo.rec_mask_coords[0][1]) > 16)):
toggle(winfo, window, ['__REC_Mask__'], state='Def')
draw_mask = False
# Apply cropping to all images
coords = winfo.rec_mask_coords
graph_size = graph.CanvasSize
for i in range(stack.z_size):
temp_img, stack.rgba_data[i] = util.adjust_image(stack.flt_data[i],
transform, stack.x_size, graph.get_size()[0])
temp_img, stack.rgba_data[i] = util.apply_crop_to_stack(coords, graph_size, stack, i)
if i == slider_val:
display_img = temp_img
else:
print('Must choose a larger mask size.')
colorwheel_graph.Erase()
window['__REC_Image_List__'].update(set_to_index=0, scroll_to_index=0)
update_slider(winfo, window, [('__REC_Image_Slider__', {"value": 7})])
winfo.rec_last_image_choice = 'Stack'
winfo.rec_image_slider_set = 7
# Draw the square mask if the region is set
elif event == '__REC_Square_Region__' and mask_button.metadata['State'] == 'Set':
util.erase_marks(winfo, graph, current_tab)
draw_mask = True
util.draw_square_mask(winfo, graph)
# Erase graph and draw the rectangular region if the region is set
elif event == '__REC_Rectangular_Region__' and mask_button.metadata['State'] == 'Set':
util.erase_marks(winfo, graph, current_tab)
winfo.rec_mask_coords = []
winfo.rec_mask_markers = []
winfo.rec_pad_info = (None, 0, 0, 0)
winfo.rec_corner1 = None
winfo.rec_corner2 = None
winfo.new_selection = True
# Clicking on graph and making markers for mask
elif event in ['__REC_Graph__', '__REC_Graph__+UP'] and mask_button.metadata['State'] == 'Set':
# Erase any previous marks
util.erase_marks(winfo, graph, current_tab)
# Draw new marks
value = values['__REC_Graph__']
if window['__REC_Square_Region__'].Get():
winfo.rec_mask_center = round(value[0]), round(value[1])
util.draw_square_mask(winfo, graph)
elif window['__REC_Rectangle_Region__'].Get():
if winfo.new_selection:
winfo.rec_corner1 = round(value[0]), round(value[1])
winfo.rec_corner2 = None
winfo.new_selection = False
elif not winfo.new_selection:
winfo.rec_corner2 = round(value[0]), round(value[1])
x_left = min(winfo.rec_corner1[0], winfo.rec_corner2[0])
x_right = max(winfo.rec_corner1[0], winfo.rec_corner2[0])
y_top = max(winfo.rec_corner1[1], winfo.rec_corner2[1])
y_bottom = min(winfo.rec_corner1[1], winfo.rec_corner2[1])
winfo.rec_mask_coords = [(x_left, y_top), (x_left, y_bottom), (x_right, y_bottom), (x_right, y_top)]
if event == '__REC_Graph__+UP':
winfo.new_selection = True
draw_mask = True
# Remove all mask coordinates from the graph and mask file
elif event == '__REC_Erase_Mask__':
# Erase any previous marks
draw_mask, adjust, display_img, mask_transform, transform = erase_mask_data(winfo, graph, current_tab,
display_img, values)
# Run PyTIE
elif event == '__REC_Run_TIE__':
# Make sure stack still exists before trying to run PyTIE
stack_path = window['__REC_Stack__'].Get()
if os_path.exists(util.join([image_dir, stack_path], '/')):
change_inp_readonly_bg_color(window, ['__REC_QC_Input__'], 'Readonly')
winfo.ptie_recon_thread = Thread(target=ptie_recon_thread,
args=(winfo, window, graph, colorwheel_graph, images, current_tab),
daemon=True)
print('--- Starting Reconstruction ---')
winfo.ptie_recon_thread.start()
else:
print('The stack has been deleted since it has been loaded. You must restart.')
# Save PyTIE
elif event == '__REC_Save_TIE__':
if winfo.rec_tie_results:
tfs = values['__REC_TFS_Combo__']
filenames, overwrite_signals, additional_vals = run_save_window(winfo, event, image_dir,
orientations=prefix,
defocus=winfo.rec_def_val, tfs=tfs)
pref, save_tie, im_dir = additional_vals
if len(overwrite_signals) > 0:
save = overwrite_signals[0]
else:
save = False
if filenames == 'close' or not filenames or not save or not save_tie:
print(f'{prefix}Exited without saving files!\n')
elif save:
winfo.ptie_recon_thread = Thread(target=ptie_save,
args=(winfo, window, image_dir, images, filenames,
pref, im_dir, save_tie),
daemon=True)
print('--- Starting Saving ---')
winfo.ptie_recon_thread.start()
else:
print(f"{prefix}Reconstruction results haven't been generated.")
# Update the arrow images
elif event == "__REC_Arrow_Set__":
arrow_transform = get_arrow_transform(window)
if (util.represents_int_above_0(arrow_transform[0]) and
util.represents_float(arrow_transform[1]) and
util.represents_float(arrow_transform[2]) and
arrow_transform[0] not in [''] and
arrow_transform[1] not in ['', '.'] and
arrow_transform[2] not in ['', '.'] and
float(arrow_transform[1]) > 0 and float(arrow_transform[2]) > 0):
# Change the vector image
arrow_transform = (int(arrow_transform[0]),
float(arrow_transform[1]),
float(arrow_transform[2]),
arrow_transform[3])
winfo.rec_past_arrow_transform = arrow_transform
hsv = window['__REC_Colorwheel__'].get() == "HSV"
color_float_array = images['color_b'].float_array
mag_x, mag_y = images['bxt'].float_array, images['byt'].float_array
v_num, v_len, v_wid, v_color = arrow_transform
v_color = v_color == 'On'
graph_size = graph.get_size()
byte_img = util.add_vectors(mag_x, mag_y, color_float_array,
v_color, hsv, v_num, v_len,
v_wid, graph_size, winfo.rec_pad_info, save=None)
shape = color_float_array.shape
im = util.FileImage(np.empty(shape), np.empty(shape),
(winfo.graph_slice[0], winfo.graph_slice[1], 1), '/vector')
im.byte_data = byte_img
winfo.rec_images['vector'] = im
image_choice = window['__REC_Image_List__'].get()[0]
if image_choice == 'Vector Im.':
display_img = im.byte_data
display_img2 = winfo.rec_colorwheel
else:
print(f'{pref}Some of the arrow values are incorrect. Check to make sure', end=' '),
print(f'that the number of arrows is an integer > 0, & the len/width are floats > 0.')
# Adjust stack and related variables
if adjust:
if winfo.rec_past_transform != transform:
stack = images['REC_Stack']
slider_val = int(values["__REC_Slider__"])
display_img, stack.rgba_data[slider_val] = util.adjust_image(stack.flt_data[slider_val],
transform, stack.x_size,
graph.get_size()[0])
util.erase_marks(winfo, graph, current_tab, full_erase=True)
if window['__REC_Square_Region__']:
util.draw_square_mask(winfo, graph)
elif window['__REC_Rectangle_Region__']:
pass
winfo.rec_past_transform = transform
winfo.rec_past_mask = mask_transform
# Change the colorwheel
if change_colorwheel:
if winfo.rec_tie_results:
colorwheel_type = window['__REC_Colorwheel__'].get()
rad1, rad2 = colorwheel_graph.get_size()
if colorwheel_type == 'HSV':
cwheel_hsv = colorwheel_HSV(rad1, background='black')
cwheel = colors.hsv_to_rgb(cwheel_hsv)
hsvwheel = True
elif colorwheel_type == '4-Fold':
cwheel = colorwheel_RGB(rad1)
hsvwheel = False
uint8_colorwheel, float_colorwheel = util.convert_float_unint8(cwheel, (rad1, rad2))
rgba_colorwheel = util.make_rgba(uint8_colorwheel[0])
winfo.rec_colorwheel = util.convert_to_bytes(rgba_colorwheel)
results = winfo.rec_tie_results
results['color_b'] = color_im(results['bxt'], results['byt'],
hsvwheel=hsvwheel, background='black')
float_array = util.slice_im(results['color_b'], (0, 0, winfo.graph_slice[0], winfo.graph_slice[1]))
uint8_data, float_data = {}, {}
uint8_data, float_data = util.convert_float_unint8(float_array, graph.get_size(),
uint8_data, float_data)
image = util.FileImage(uint8_data, float_data, (winfo.graph_slice[0], winfo.graph_slice[1], 1), 'color_b',
float_array=float_array)
image.byte_data = util.vis_1_im(image)
winfo.rec_images['color_b'] = image
# Add the vector image
color_float_array = float_array
mag_x, mag_y = images['bxt'].float_array, images['byt'].float_array
vector_color = window['__REC_Arrow_Color__'].get()
vector_num = int(window['__REC_Arrow_Num__'].get())
vector_len, vector_wid = int(window['__REC_Arrow_Len__'].get()), int(window['__REC_Arrow_Wid__'].get())
graph_size = graph.get_size()
byte_img = util.add_vectors(mag_x, mag_y, color_float_array,
True, hsvwheel, vector_num, vector_len,
vector_wid, graph_size, winfo.rec_pad_info, save=None)
shape = float_array.shape
im = util.FileImage(np.empty(shape), np.empty(shape),
(winfo.graph_slice[0], winfo.graph_slice[1], 1), '/vector')
im.byte_data = byte_img
winfo.rec_images['vector'] = im
if window['__REC_Image_List__'].get()[0] == 'Color':
display_img = image.byte_data
display_img2 = winfo.rec_colorwheel
elif window['__REC_Image_List__'].get()[0] == 'Vector Im.' and vector_color == 'On':
display_img = im.byte_data
display_img2 = winfo.rec_colorwheel
winfo.rec_last_colorwheel_choice = colorwheel_choice
# Reset page
if event == "__REC_Reset_Img_Dir__":
if winfo.ptie_init_thread is not None and winfo.ptie_init_thread.is_alive():
winfo.ptie_init_thread = None
if winfo.ptie_recon_thread is not None and winfo.ptie_recon_thread.is_alive():
winfo.ptie_recon_thread = None
reset(winfo, window, current_tab)
# Enable any elements if need be
special_enable_disable(winfo, window)
# Redraw all
if display_img:
redraw_graph(graph, display_img)
if display_img2:
redraw_graph(colorwheel_graph, display_img2)
if draw_mask:
util.draw_mask_points(winfo, graph, current_tab)
if winfo.rec_mask_coords and mask_button.metadata['State'] == 'Def':
text = ' Set '
mask_color = 'green'
font = 'Times 18 bold'
else:
text = 'Unset'
mask_color = 'black'
font = 'Times 17'
window['__REC_Mask_Text__'].update(value=text, text_color=mask_color, font=font)
# -------------- Save Window --------------#
[docs]def check_overwrite(winfo: Struct, save_win: sg.Window, true_paths: List[str],
orientations: List[str], image_dir: str,
im_type: str, event: str, tfs) -> List[bool]:
"""Check whether the paths listed in the log box for
each image will be overwritten.
Args:
winfo: The data structure holding all information about
windows and GUI.
save_win: The save window element
true_paths: A list of path names that will be
checked if they exist.
orientations: A list of strings that represent
the orientations of the image ('flip',
'unflip', 'stack', etc.)
image_dir: The path of the current working directory of images
im_type: Image type (.bmp, .tiff, etc.)
event: The key for the values dictionary that represents
an event in the window.
tfs: The through focal series value determined by the user for BUJ or LS.
Returns:
overwrite_signals: The boolean values for overwriting the files.
"""
# If file exists notify user and give option to change name
update_values(winfo, save_win, [('__save_win_log__', '')])
overwrite_signals = [None]*len(true_paths)
save_enable = True
if event == '__REC_Save_TIE__':
overwrite_box = save_win[f'__save_win_overwrite1__'].Get()
rec_tie_dont_overwrite_state = False
rec_tie_dont_overwrite_text = 'Some files already exist. Check overwrite box or change name.'
for i in range(len(true_paths)):
text = ''
if event == '__REC_Save_TIE__':
path = f'{image_dir}/images/{true_paths[i]}'
else:
path = f'{image_dir}/{true_paths[i]}'
exists = os_path.exists(path)
# If no orientation, this removes extra space in insertion for log
if event != '__REC_Save_TIE__':
overwrite_box = save_win[f'__save_win_overwrite{i+1}__'].Get()
if orientations[i]:
insertion = f'{orientations[i]}'
else:
insertion = im_type
# Exists but not overwrite
if exists and not overwrite_box:
text = f'''The {insertion} file already exists. Check overwrite checkbox if you want to save anyway.'''
overwrite_signals[i] = False
save_enable = False
# File already exists but will be overwritten
elif exists and overwrite_box:
text = f'The {insertion} file will be overwritten.'
overwrite_signals[i] = True
# Doesn't exist, don't overwrite
elif not exists:
text = f'The {insertion} file will be saved.'
overwrite_signals[i] = True
elif event == '__REC_Save_TIE__':
if tfs != 'Single' or (tfs == 'Single' and 'phase_e' not in true_paths[i] and
'dIdZ_e' not in true_paths[i]):
if exists and not overwrite_box:
rec_tie_dont_overwrite_state = True
save_enable = False
overwrite_signals = [False]
elif exists and overwrite_box:
text = f'''The {true_paths[i]} file will be overwritten.'''
overwrite_signals = [True]
elif not exists:
text = f'The {true_paths[i]} file will be saved.'
overwrite_signals = [True]
# Update save window
current_log_text = save_win['__save_win_log__'].Get()
new_log_text = current_log_text + text
update_values(winfo, save_win, [('__save_win_log__', new_log_text.strip())])
if rec_tie_dont_overwrite_state:
update_values(winfo, save_win, [('__save_win_log__', rec_tie_dont_overwrite_text.strip())])
if save_enable:
enable_elements(winfo, save_win, ['__save_win_save__'])
else:
disable_elements(save_win, ['__save_win_save__'])
return overwrite_signals
[docs]def save_window_values(save_win: sg.Window, num_paths: int, event: str,
orientations: List[str], defocus: Optional[str] = None, tfs: Optional[str] = None,
true_paths: Optional[List] = None) -> List[str]:
"""Sets ups the save window layout.
Args:
save_win: The representation of the save window.
num_paths: The number of paths, to create the number
of overwrite checkboxes and true_path
input elements.
event: The save event from the main GUI window.
orientations: A list of strings that represent
the orientations of the image ('flip',
'unflip', 'stack', etc.)
defocus: The defocus value for the image if its REC.
tfs: The selected through focal series.
true_paths: The true_paths for the files to be saved.
Returns:
true_paths: The list containing the full path names.
"""
# Comb through all input fields and pull current path name
if event != '__REC_Save_TIE__':
true_paths = []
for i in range(1, num_paths + 1):
true_paths.append(save_win[f'__save_win_filename{i}__'].Get())
elif event == '__REC_Save_TIE__':
save_choice = save_win['__save_rec_combo__'].Get()
pref = save_win[f'__save_win_prefix__'].Get()
if save_choice != '----':
true_paths = []
if save_choice == 'Color':
stop = 2
elif save_choice == 'Full Save':
stop = 10
elif save_choice == 'Mag. & Color':
stop = 4
elif save_choice == 'No Save':
stop = 0
elif save_choice == 'Manual':
stop = None
# Batch choose files to save
if stop is not None:
for i in range(stop):
true_paths.append(util.join([pref, str(defocus), orientations[i]], '_'))
# Manually select the files to save and pass the orientations into true pqths
else:
save_win.Hide()
file_choice_layout = file_choice_ly(tfs)
file_choice_win = sg.Window('File Choice Window', file_choice_layout, size=(220, 300), element_justification="left",
finalize=True, icon=get_icon())
while True:
ev3, vals3 = file_choice_win.Read(timeout=400)
if ev3 == 'Exit' or ev3 is None or ev3 in ['fc_win_submit', 'fc_win_close']:
file_choices = [0]
item_list = ['color_b', 'byt', 'bxt',
'bbt', 'dIdZ_e', 'dIdZ_m', 'inf_im',
'phase_e', 'phase_b', 'arrow_colormap',
'bw_arrow_colormap']
for key in item_list:
if tfs == 'Unflip/Flip' or (tfs == 'Single' and 'phase_e' not in key and 'dIdZ_e' not in key):
if file_choice_win[key].Get():
file_choices.append(item_list.index(key) + 1)
if ev3 == 'fc_win_close' or len(file_choices) == 1:
file_choices = []
file_choice_win.Close()
save_win['__save_rec_combo__'].Update(value='----')
break
for i in file_choices:
true_paths.append(f"{pref}_{defocus}_{orientations[i]}")
save_win.UnHide()
if save_choice in ['Mag. & Color', 'Full Save']:
true_paths.append(util.join([pref, str(defocus), orientations[10]], '_'))
true_paths.append(util.join([pref, str(defocus), orientations[11]], '_'))
return true_paths
[docs]def run_save_window(winfo: Struct, event: str, image_dir: str,
orientations: Optional[Union[str, List[str]]] = None,
defocus: Optional[str] = None,
tfs: str = 'Unflip/Flip') -> Tuple[List[str], List[bool], Optional[Tuple[str, bool, str]]]:
"""Executes the save window.
Args:
winfo: The data structure holding all information about
windows and GUI.
event: The key for the values dictionary that represents
an event in the window.
image_dir: The working directory where image will be saved
orientations: List of the orientations or filetypes to categorize the saved
file ('flip', 'unflip', 'stack', '').
defocus: The value for the defocus if running reconstruction.
tfs: The through focal series value chosen for LS or BUJ.
Returns:
filenames: The list of filenames to give the saved images.
overwrite_signals: List of booleans of whether to overwrite files.
prefix: The prefix for the tab to print to.
save_tie: Boolean value of whether to execute saving of reconstructed images.
im_dir: The image directory to save reconstructed images to.
"""
# Create layout of save window
vals = save_window_ly(event, image_dir, orientations, tfs=tfs, tie_prefix=winfo.rec_tie_prefix)
window_layout, im_type, file_paths = vals[0:3]
orientations, inputs = vals[3:]
save_win = sg.Window('Save Window', window_layout, finalize=True, icon=get_icon())
for key in inputs:
save_win[key].Update(move_cursor_to='end')
save_win[key].Widget.xview_moveto(1)
winfo.save_win = save_win
winfo.window.Hide()
if winfo.output_window_active:
winfo.output_window.Disappear()
winfo.output_window.Hide()
if event == '__REC_Save_TIE__':
true_paths = save_window_values(save_win, len(file_paths), event, orientations, defocus, tfs)
else:
true_paths = save_window_values(save_win, len(file_paths), event, orientations, defocus)
# Run save window event handler
# Initiate event allows successful creation and reading of window
overwrite_signals = []
ev2 = 'Initiate'
while True:
if ev2 != 'Initiate':
ev2, vals2 = save_win.Read(timeout=400)
# Saving TIE images
if event == '__REC_Save_TIE__':
prefix = save_win[f'__save_win_prefix__'].Get()
index = save_win['__save_win_filename1__'].Get().rfind('/')
im_dir = save_win['__save_win_filename1__'].Get()[index + 1:]
save_choice = save_win['__save_rec_combo__'].Get()
if save_choice == 'Color':
save_tie = 'color'
elif save_choice == 'Full Save':
save_tie = True
elif save_choice == 'Mag. & Color':
save_tie = 'b'
elif save_choice == 'No Save':
save_tie = False
elif save_choice in ['Manual', '----']:
save_tie = 'manual'
# Getting full paths to the images and checking if they need to be overwritten
filenames = []
if ev2 and ev2 != 'Exit':
true_paths = save_window_values(save_win, len(file_paths), event, orientations, defocus, tfs,
true_paths)
if ev2 and 'TIMEOUT' not in ev2:
overwrite_signals = check_overwrite(winfo, save_win, true_paths, orientations,
image_dir, im_type, event, tfs)
# Exit or save pressed
if not ev2 or ev2 in ['Exit', '__save_win_save__']:
if winfo.output_window_active:
winfo.output_window.Reappear()
winfo.output_window.UnHide()
winfo.window.UnHide()
save_win.Close()
if ev2 == '__save_win_save__':
for i in range(len(true_paths)):
if event == '__REC_Save_TIE__':
path = f'{image_dir}/images/{true_paths[i]}'
else:
path = f'{image_dir}/{true_paths[i]}'
filenames.append(path)
break
if ev2 == 'Initiate':
ev2 = None
# Return values based off saving reconstructed images or not.
if event != '__REC_Save_TIE__':
return filenames, overwrite_signals, None
elif event == '__REC_Save_TIE__':
return filenames, overwrite_signals, (prefix, save_tie, im_dir)
# -------------- Main Event Handler and run GUI --------------#
[docs]def event_handler(winfo: Struct, window: sg.Window) -> None:
""" The event handler handles all button presses, mouse clicks, etc.
that can take place in the app. It takes the SG window and the struct
containing all window data as parameters.
Args:
winfo: The data structure holding all information about
windows and GUI.
window: The element representing the main GUI window.
Returns:
None
"""
# Create output_window
output_window = output_ly()
output_window.Hide()
# Initialize window, bindings, and event variables
init(winfo, window, output_window)
for key in winfo.keys['input']:
window[key].Update(move_cursor_to='end')
window[key].Widget.xview_moveto(1)
set_pretty_focus(winfo, window, 'Window Click')
# Set up the output log window and redirecting std_out
log_line = 0
with StringIO() as winfo.buf, StringIO() as winfo.fiji_buf:
with redirect_stdout(winfo.buf):
# Run event loop
bound_click = True
# bound_scroll = False
close = None
while True:
# Capture events
event, values = window.Read(timeout=50)
# Break out of event loop
if event is None or close == 'close' or event == 'Exit::Exit1': # always, always give a way out!
if winfo.ptie_init_thread is not None:
if winfo.ptie_init_thread.is_alive():
winfo.ptie_init_thread.join(0.1)
if winfo.ptie_recon_thread is not None:
if winfo.ptie_recon_thread.is_alive():
winfo.ptie_recon_thread.join(0.1)
winfo.ptie_recon_thread = None
output_window.close()
window.close()
break
if event in ['Show (Control-l)::Log', 'Show Log'] and not winfo.output_window_active:
winfo.output_window_active = True
output_window.Reappear()
output_window.UnHide()
elif event in ['Show (Control-l)::Log', 'Show Log'] and winfo.output_window_active:
output_window.BringToFront()
elif event in ['Hide (Control-h)::Log', 'Hide Log'] and winfo.output_window_active:
winfo.output_window_active = False
output_window.Hide()
output_window.Disappear()
set_pretty_focus(winfo, window, 'Window Click')
# About section was opened
if event == 'Documentation::Documentation':
try:
webbrowser.open('https://pylorentztem.readthedocs.io/en/latest/')
except:
print('*** ATTEMPT TO ACCESS ABOUT PAGE FAILED ***')
print('*** CHECK INTERNET CONNECTION ***')
# Disable window clicks if creating mask or setting subregion
if ((winfo.true_element == '__REC_Graph__' and bound_click and
window['__REC_Mask__'].metadata['State'] == 'Set')):
window.TKroot.unbind("<Button-1>")
bound_click = False
elif (not bound_click and winfo.true_element != '__REC_Graph__'):
winfo.window.bind("<Button-1>", 'Window Click')
bound_click = True
# Make sure input element that just display names can't be typed in
if event in winfo.keys['read_only_inputs']:
state = window[event].metadata['State']
text = window[event].metadata[state]
window[event].update(value=text)
# Check which tab is open and execute events regarding that tab
current_tab = winfo.current_tab = get_open_tab(winfo, winfo.pages, event)
if current_tab == "home_tab":
run_home_tab(winfo, window, event, values)
elif current_tab == "reconstruct_tab":
run_reconstruct_tab(winfo, window, current_tab, event, values)
# Show loading spinners if necessary def init
if winfo.ptie_init_thread is not None:
if (window['__REC_FLS_Spinner__'].metadata['State'] == 'Def' and
not winfo.ptie_init_spinner_active):
activate_spinner(window, '__REC_FLS_Spinner__')
winfo.ptie_init_spinner_active = True
elif winfo.ptie_init_thread is None:
if window['__REC_FLS_Spinner__'].metadata['State'] == 'Set':
deactivate_spinner(window, '__REC_FLS_Spinner__')
winfo.ptie_init_spinner_active = False
if winfo.ptie_recon_thread is not None:
if (window['__REC_PYTIE_Spinner__'].metadata['State'] == 'Def' and
not winfo.ptie_recon_spinner_active):
activate_spinner(window, '__REC_PYTIE_Spinner__')
winfo.ptie_recon_spinner_active = True
elif winfo.ptie_recon_thread is None:
if window['__REC_PYTIE_Spinner__'].metadata['State'] == 'Set':
deactivate_spinner(window, '__REC_PYTIE_Spinner__')
winfo.ptie_recon_spinner_active = False
# Update loading spinners spinners
active_spinners = []
if winfo.ptie_init_spinner_active:
active_spinners.append('__REC_FLS_Spinner__')
if winfo.ptie_recon_spinner_active:
active_spinners.append('__REC_PYTIE_Spinner__')
for spinner_key in active_spinners:
spinner_fn = window[spinner_key].metadata['Set']
window[spinner_key].UpdateAnimation(spinner_fn)
# Set the focus of the GUI to reduce interferences
set_pretty_focus(winfo, window, event)
# Copying and pasting text from output windows
if winfo.output_window_active:
output_event, output_values = output_window.Read(timeout=0)
if output_event in ['MAIN_OUTPUT+FOCUS_IN+', 'FIJI_OUTPUT+FOCUS_IN+']:
key = output_event[:-10]
widget = winfo.output_window[key].Widget
widget.bind("<1>", widget.focus_set())
disable_elements(output_window, [key])
winfo.output_focus_active = True
winfo.active_output_focus_el = key
elif output_event in ['MAIN_OUTPUT+FOCUS_OUT+', 'FIJI_OUTPUT+FOCUS_OUT+']:
key = output_event[:-11]
widget = winfo.output_window[key].Widget
widget.unbind("<1>")
enable_elements(winfo, output_window, [key])
winfo.output_focus_active = False
winfo.active_output_focus_el = None
if event != '__TIMEOUT__' and 'HOVER' not in event:
winfo.invis_graph.SetFocus()
elif output_event in ['output_tabgroup'] and winfo.output_focus_active:
key = winfo.active_output_focus_el
widget = winfo.output_window[key].Widget
widget.unbind("<1>")
enable_elements(winfo, output_window, [key])
winfo.output_focus_active = False
winfo.active_output_focus_el = None
if output_event in ['MAIN_OUTPUT_AUTOSCROLL']:
autoscroll_state = output_window[output_event].get()
if 'MAIN_OUTPUT' in output_event:
key = 'MAIN_OUTPUT'
output_window[key].Update(autoscroll=autoscroll_state)
if output_event in ['MAIN_OUTPUT_HIDE', 'Output Hide Log']:
winfo.output_window_active = False
output_window.Hide()
output_window.Disappear()
set_pretty_focus(winfo, window, 'Window Click')
elif '__TIMEOUT__' not in output_event:
set_pretty_focus(winfo, output_window, output_event)
sys_stdout.flush()
output_txt = winfo.buf.getvalue().split('\n')
i = 0
for line in output_txt:
if log_line <= i:
log_line = i
if not(line.isspace() or line.strip() == ''):
if (not (line.startswith('REC') or line.startswith('HOM') or
line.startswith('***')) and line != '\n'):
line = 'REC: ' + line
output_window['MAIN_OUTPUT'].update(value=f'{line}\n', append=True)
i += 1
[docs]def run_GUI() -> None:
"""Main run function. Takes in the style and defaults for GUI."""
# Create the layouts
DEFAULTS = defaults()
sg.theme('BlueMono')
background_color = sg.theme_background_color()
sg.SetOptions(margins=(0, 0), element_padding=((0, 0), (0, 0)),
border_width=0, font=('Times New Roman', '16'))
sys_layout = [[sg.Text('')]]
scaling_window = sg.Window('Window Title', sys_layout, alpha_channel=0, no_titlebar=True,
finalize=True, icon=get_icon())
scaling_window.TKroot.tk.call('tk', 'scaling', 1)
scaling_window.close()
window = window_ly(background_color, DEFAULTS)
# This snippet allows use of menu bar without having to switch windows first on Mac
if platform() == 'Darwin':
subprocess.call(["/usr/bin/osascript", "-e", 'tell app "Finder" to set frontmost of process "Finder" to true'])
subprocess.call(["/usr/bin/osascript", "-e", 'tell app "Finder" to set frontmost of process "python" to true'])
# Create data structure to hold variables about GUI, alignment and reconstruction.
winfo = Struct()
# Event handling
event_handler(winfo, window)
if __name__ == '__main__':
run_GUI()