Source code for kalmus.tkinter_windows.MainWindowVersion2

"""
MainWindow Class
Version2
"""

import tkinter
from tkinter.messagebox import askokcancel, showinfo, showerror

from kalmus.tkinter_windows.GenerateBarcodeWindow import GenerateBarcodeWindow
from kalmus.tkinter_windows.SaveBarcodeWindow import SaveBarcodeWindow
from kalmus.tkinter_windows.LoadStackWindow import LoadStackWindow
from kalmus.tkinter_windows.LoadJsonWindow import LoadJsonWindow
from kalmus.tkinter_windows.ReshapeBarcodeWindow import ReshapeBarcodeWindow
from kalmus.tkinter_windows.gui_utils import get_time, paint_hue_hist, update_axes_title, update_axes_ticks, resource_path
from kalmus.tkinter_windows.plot_barcodes_windows.WhichBarcodeInspectWindow import WhichBarcodeInspectWindow
from kalmus.tkinter_windows.StatsInfoWindow import StatsInfoWindow
from kalmus.tkinter_windows.SaveImageWindow import SaveImageWindow
from kalmus.tkinter_windows.meta_info_windows.WhichBarcodeCheckMeta import WhichBarcodeCheckMeta
from kalmus.tkinter_windows.time_points_windows.CheckTimePointWindow import CheckTimePointWindow

import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)

from skimage.color import rgb2hsv

import numpy as np

import copy
import matplotlib

# General setup of the figure
font = {'family': 'DejaVu Sans',
        'size': 8}

matplotlib.rc('font', **font)


[docs]class MainWindow(): """ MainWindow Class. The main GUI window for user to interact with the Kalmus software. Has two displays for the barcodes and the histograms of their hue/brightness values. Has all buttons to the subwindow of the kalmus software. """ def __init__(self, barcode_tmp, barcode_gn, figsize=(12, 5), dpi=100): """ Initialize :param barcode_tmp: The temporary barcode object for software initialization :param barcode_gn: The barcode generator object :param figsize: The size of the plotted figure :param dpi: The dpi of the plotted figure """ # Initialize the barcode memory stack self.barcodes_stack = {"default": copy.deepcopy(barcode_tmp)} # Copy over the barcodes self.barcode_1 = copy.deepcopy(barcode_tmp) self.barcode_2 = copy.deepcopy(barcode_tmp) # Initialize the barcode's meta data to none self.barcode_1.meta_data = {} self.barcode_2.meta_data = {} # Get the barcode generator self.barcode_gn = barcode_gn # Initialize the window self.root = tkinter.Tk() self.root.configure(bg='#85C1FA') self.root.wm_title("KALMUS Version 1.3.13b1") self.root.iconbitmap(resource_path("kalmus_icon.ico")) self.dpi = dpi # Initialize the figure self.fig, self.ax = plt.subplots(2, 2, figsize=figsize, dpi=self.dpi, gridspec_kw={'width_ratios': [2.8, 1]}, sharex='col', sharey='col') update_axes_title(self.ax, self.barcode_1, self.barcode_2) # Plot the barcodes into the figure self.barcode_display_1 = self.ax[0][0].imshow(self.barcode_1.get_barcode().astype("uint8")) self.barcode_display_2 = self.ax[1][0].imshow(self.barcode_2.get_barcode().astype("uint8")) # Normalize the barcode before converting the color map normalized_barcode_1 = self.barcode_1.get_barcode().astype("float") / 255 hsv_colors_1 = rgb2hsv(normalized_barcode_1.reshape(-1, 1, 3)) hue_1 = hsv_colors_1[..., 0] * 360 # The step of the histogram bin_step = 5 N, bins, patches = self.ax[0][1].hist(hue_1[:, 0], bins=(np.arange(0, 361, bin_step))) paint_hue_hist(bin_step, patches) # Update the hue histogram self.ax[0][1].set_xticks(np.arange(0, 361, 30)) self.ax[0][1].set_xlabel("Color Hue (0 - 360)") self.ax[0][1].set_ylabel("Number of frames") normalized_barcode_2 = self.barcode_2.get_barcode().astype("float") / 255 hsv_colors_2 = rgb2hsv(normalized_barcode_2.reshape(-1, 1, 3)) hue_2 = hsv_colors_2[..., 0] * 360 N, bins, patches = self.ax[1][1].hist(hue_2[:, 0], bins=(np.arange(0, 361, bin_step))) # Paint the hue histogram where each bin of the histogram is in the color of the corresponding hue paint_hue_hist(bin_step, patches) self.ax[1][1].set_xticks(np.arange(0, 361, 30)) self.ax[1][1].set_xlabel("Color Hue (0 - 360)") self.ax[1][1].set_ylabel("Number of frames") # Use tight layout plt.tight_layout() update_axes_ticks(self.barcode_1, self.barcode_2, self.ax) # Draw the canvas self.canvas = FigureCanvasTkAgg(self.fig, master=self.root) # A tk.DrawingArea. self.canvas.draw() self.canvas.mpl_connect('button_press_event', self.time_pick) # Use tkinter Frame to organize the figure widget toolbarFrame = tkinter.Frame(master=self.root, width=500, height=40) toolbarFrame.grid(row=8, column=2, sticky="nse", rowspan=2) toolbarFrame.pack_propagate(False) # Set up the tool bar of the plotted figure self.toolbar = NavigationToolbar2Tk(self.canvas, toolbarFrame) self.toolbar.update() # Position the canvas/plotted figure into the window self.canvas.get_tk_widget().grid(row=0, column=1, rowspan=8, columnspan=5) r = g = b = 0 self.color_swatch = tkinter.Label(master=self.root, text="", bg=f'#{r:02x}{g:02x}{b:02x}', width=4, height=2, borderwidth=1.5, relief="solid") self.color_swatch.grid(row=8, column=3, rowspan=1, padx=0, sticky=tkinter.E) self.canvas.mpl_connect('motion_notify_event', self.display_color) self.color_label = tkinter.Label(master=self.root, text="Red = {:>3d} " "Green = {:>3d} " "Blue = {:>3d}\n".format(0, 0, 0) + "Frame: {:>8d} ".format(0) + "Time: {:02d}:{:02d}:{:02d} ".format(0, 0, 0), font=("Arial", 8), width=32, bg='#85C1FA', padx=0, pady=0, justify=tkinter.LEFT) self.color_label.grid(row=8, column=4, rowspan=1, padx=0, sticky=tkinter.W) self.generate_window_opened = False # Button to generate the barcode button_generate = tkinter.Button(master=self.root, text="Generate Barcode", command=self.generate_barcode) button_generate.grid(row=0, column=0, padx=3) # Button to load the barcode from existed json files button_load = tkinter.Button(master=self.root, text="Load JSON", command=self.load_json_barcode) button_load.grid(row=1, column=0) # Button to load the barcode from the memory stack button_load_stack = tkinter.Button(master=self.root, text="Load Memory", command=self.load_stack_barcode) button_load_stack.grid(row=2, column=0) # Button to reshape the barcode displayed in the main window button_reshape_barcode = tkinter.Button(master=self.root, text="Reshape Barcode", command=self.reshape_barcode) button_reshape_barcode.grid(row=3, column=0) # Button to save the barcode into json files button_save_json = tkinter.Button(master=self.root, text="Save JSON", command=self.save_barcode_on_stack) button_save_json.grid(row=4, column=0) # Button to save the barcode displayed into the image button_save_image = tkinter.Button(master=self.root, text="Save Image", command=self.save_image_from_display) button_save_image.grid(row=5, column=0) # Button to inspect the barcode in details button_barcode = tkinter.Button(master=self.root, text="Inspect Barcode", command=self.show_barcode) button_barcode.grid(row=6, column=0) # Button to show the statistics of the barcode button_stats_info = tkinter.Button(master=self.root, text="Stats Info", command=self.stats_info) button_stats_info.grid(row=7, column=0) # Button to quit the main window button_quit = tkinter.Button(master=self.root, text="Quit", command=self.close_window) button_quit.grid(row=8, column=0) # Button to check the meta data of the displayed barcodes button_check_meta = tkinter.Button(master=self.root, text="Check Meta Info", command=self.check_meta_info) button_check_meta.grid(row=8, column=5, sticky=tkinter.W) # Close the window mainloop if user try to close the window self.root.protocol("WM_DELETE_WINDOW", self.close_window) # Start the main window self.root.mainloop()
[docs] def close_window(self): """ close the Mainwindow. Check if the Generate Barcode window is still open before quiting the Main program. Return (cancel the quit) if the Generate Barcode window is still open. """ # Check if generate barcode window is opened if self.generate_window_opened: # If it is opened show an error showerror("Generate Barcode Window is Opened", "Generate Barcode window is still opened!\n" "Please close the Generate Barcode window before Quit.") return # Otherwise check if user want to quit the software quit_software = askokcancel("Quit KALMUS", "Are you sure you want to close the KALMUS?\n" "All unsaved results will be lost.") # Quit if yes if quit_software: self.quit()
[docs] def quit(self): """ Quit the main window """ self.root.quit() self.root.destroy()
[docs] def check_meta_info(self): """ Instantiate the WhichBarcodeCheckMeta window """ WhichBarcodeCheckMeta(self.barcode_1, self.barcode_2, self.barcodes_stack)
[docs] def show_barcode(self): """ Instantiate the WhichBarcodeInspectWindow """ WhichBarcodeInspectWindow(self.barcode_1, self.barcode_2, dpi=self.dpi, figsize=(7.6, 4.3))
[docs] def load_json_barcode(self): """ Instantiate the LoadJsonWindow """ LoadJsonWindow(self.barcode_gn, self.barcode_1, self.barcode_2, self.ax, self.canvas, self.barcodes_stack)
[docs] def load_stack_barcode(self): """ Instantiate the LoadStackWindow """ LoadStackWindow(self.barcodes_stack, self.barcode_1, self.barcode_2, self.ax, self.canvas)
[docs] def reshape_barcode(self): """ Instantiate the ReshapeBarcodeWindow """ ReshapeBarcodeWindow(self.barcode_1, self.barcode_2, self.ax, self.canvas)
[docs] def stats_info(self): """ Instantiate the StatsInfoWindow """ StatsInfoWindow(self.barcode_1, self.barcode_2)
[docs] def generate_barcode(self): """ Instantiate the GenerateBarcodeWindow """ if not self.generate_window_opened: self.generate_window_opened = True GenerateBarcodeWindow(self.barcode_gn, self.barcodes_stack) self.generate_window_opened = False else: showinfo("Generate Barcode window is Opened", "Generate Barcode Window is already opened.")
[docs] def save_barcode_on_stack(self): """ Instantiate the SaveBarcodeWindow """ SaveBarcodeWindow(self.barcodes_stack)
[docs] def save_image_from_display(self): """ Instantiate the SaveImageWindow """ SaveImageWindow(self.barcode_1, self.barcode_2)
[docs] def display_color(self, event): if event.xdata and event.ydata: ix, iy = int(event.xdata + 0.5), int(event.ydata + 0.5) else: return for i, axe in enumerate(self.ax[:, 0]): if axe == event.inaxes: # Check if it is plotted barcode 1 or plotted barcode 2 if i == 0: barcode = self.barcode_1 else: barcode = self.barcode_2 barcode_shape = barcode.get_barcode().shape if 0 <= iy < barcode_shape[0] and 0 <= ix < barcode_shape[1]: if barcode.barcode_type == "Color": r, g, b = barcode.get_barcode().astype("uint8")[iy, ix] self.color_swatch.config(bg=f'#{r:02x}{g:02x}{b:02x}') color_label_text = "Red = {:>3d} " \ "Green = {:>3d} " \ "Blue = {:>3d}\n".format(r, g, b) elif barcode.barcode_type == "Brightness": r = g = b = barcode.get_barcode().astype("uint8")[iy, ix] self.color_swatch.config(bg=f'#{r:02x}{g:02x}{b:02x}') color_label_text = "Brightness = {:>3d}\n".format(r) frame, time_hr, time_min, time_sec = get_time(barcode, ix, iy) color_label_text = color_label_text + "Frame: {:>8d} ".format(frame) + \ "Time: {:02d}:{:02d}:{:02d} ".format(time_hr, time_min, time_sec) self.color_label.config(text=color_label_text)
[docs] def time_pick(self, event): """ Pick the time at a point of the plotted barcode if user double click on that point :param event: matplotlib event. Only catch double click event """ # If user is double click on the graph if event.dblclick: # Try get the x and y position of the clicked point try: ix, iy = int(event.xdata + 0.5), int(event.ydata + 0.5) except Exception: return # find which axis of the graph does the clicked point belong to for i, axe in enumerate(self.ax[:, 0]): if axe == event.inaxes: # Check if it is plotted barcode 1 or plotted barcode 2 if i == 0: barcode = self.barcode_1 else: barcode = self.barcode_2 # Make sure the point is within the plotted barcode barcode_shape = barcode.get_barcode().shape if 0 <= iy < barcode_shape[0] and 0 <= ix < barcode_shape[1]: # Instantiate the CheckTimePointWindow CheckTimePointWindow(barcode, mouse_x=ix, mouse_y=iy)