import pkgutil
try:
from tkinter import Button, Canvas, Frame, Label, PhotoImage, StringVar
from tkinter.colorchooser import askcolor
from tkinter.filedialog import askopenfilename, asksaveasfilename
from tkinter.messagebox import (
askretrycancel,
askyesno,
showinfo,
showwarning,
)
from tkinter.simpledialog import askstring
except (ImportError, ModuleNotFoundError) as error:
print(
f"Unable to lauch app window because:\n\n\t* {error}.\n\n"
"Please visit https://tkdocs.com/tutorial/install.html for"
" help installing it.\n\nYou can still use the eda-report command. "
"Try 'eda-report -h' for more details.\n"
)
exit()
from eda_report._read_file import df_from_file
from eda_report._validate import _validate_groupby_variable
from eda_report.document import ReportDocument
from eda_report.exceptions import GroupbyVariableError
background_image = pkgutil.get_data(__name__, "images/background.png")
icon = pkgutil.get_data(__name__, "images/icon.png")
description = (
"Speed up exploratory data analysis & reporting.\n\n"
"Automatically analyze files, and get a Word report complete with "
"summary statistics and graphs."
)
[docs]
class EDAGUI(Frame): # pragma: no cover
"""The blueprint for the :mod:`tkinter` - based *graphical user
interface* to the application.
.. figure:: _static/screencast.*
:alt: an image of the graphical user interface
The "Select a file" button launches a *file-dialog* to navigate to and
select a file to analyze.
If a valid file is selected, *text-input widgets* and a *color-picker
tool* pop up to help set the report's *title*,
*target/groupby variable(optional)* and *graph color*.
Afterwards, a final file-dialog appears to help set the destination
for the generated report.
.. tip::
For help with `Tk` - related issues, consider visiting `TkDocs`_.
.. _`TkDocs`: https://tkdocs.com/index.html
"""
def __init__(self, master=None, **kwargs) -> None:
super().__init__(master)
self.master.title("eda-report")
self.master.geometry("560x320")
self.master.resizable(False, False) # Fix window size
self.master.wm_iconphoto(True, PhotoImage(data=icon))
self._create_widgets()
self.pack()
def _create_widgets(self) -> None:
"""Creates the widgets for the graphical user interface: A Tk *Frame*
with the *canvas(background image)*, *introductory text*, and a
*button* to select files to analyze.
"""
self.canvas = Canvas(self, width=560, height=320)
# Set background image
self.bg_image = PhotoImage(data=background_image)
self.canvas.create_image((0, 0), image=self.bg_image, anchor="nw")
# Add title
self.canvas.create_text(
(70, 30),
anchor="nw",
fill="black",
font=("Courier", 28, "bold"),
text="eda-report",
)
# Add description
self.canvas.create_text(
(40, 90),
anchor="nw",
fill="black",
font=("Courier", 12),
text=description,
width=480,
)
# Add a button to select input file
self.button = Button(
self,
bg="#204060",
command=self._create_report,
default="active",
fg="white",
font=("Courier", 11),
relief="flat",
text="Select a file",
)
self.canvas.create_window(
(180, 220), anchor="nw", height=40, width=200, window=self.button
)
# Display current action
self.current_action = StringVar()
self.display_current_action = Label(
self,
bg="#c0d6e3",
font=("Courier", 10, "italic"),
textvariable=self.current_action,
)
self.canvas.create_window(
(140, 280),
anchor="nw",
window=self.display_current_action,
)
self.canvas.pack()
def _create_report(self) -> None:
"""Collects input from the graphical user interface, and uses the
:class:`~eda_report.document.ReportDocument` object to generate a
report.
"""
self.current_action.set("Waiting for input file...")
self._get_data_from_file()
if self.data is not None:
self.current_action.set("Waiting for report title...")
self._get_report_title()
self.current_action.set("Waiting for group-by variable...")
self._get_groupby_variable()
self.current_action.set("Waiting for graph color...")
self._get_graph_color()
self.current_action.set("Analysing data & compiling the report...")
self._get_save_as_name()
# Generate and save the report using the collected arguments
ReportDocument(
self.data,
title=self.report_title,
graph_color=self.graph_color,
output_filename=self.save_name,
groupby_variable=self.groupby_variable,
)
self.current_action.set("")
showinfo(message=f"Done! Report saved as {self.save_name!r}.")
# Clear data to free up memory
del self.data
def _get_data_from_file(self, retries: int = 1) -> None:
"""Creates a file dialog to help navigate to and select a file to
analyze.
Args:
retries (int, optional): Number of additional prompts, if input is
invalid.
"""
file_name = askopenfilename(
title="Select a file to analyze",
filetypes=(
("All supported formats", ("*.csv", "*.xlsx")),
("csv", "*.csv"),
("excel", "*.xlsx"),
),
)
if file_name:
self.data = df_from_file(file_name)
elif retries > 0:
if askretrycancel(message="Please select a file to continue"):
self._get_data_from_file(retries - 1)
else:
# No data if retry prompt is cancelled
self.data = None
else:
# No data if no file is selected and retry has been used up
self.data = None
def _get_report_title(self) -> None:
"""Capture text input for the desired report title."""
report_title = askstring(
title="Report Title",
prompt="Please enter your preferred title for the report:",
initialvalue="Exploratory Data Analysis Report",
)
self.report_title = report_title or "Exploratory Data Analysis Report"
def _get_groupby_variable(self) -> None:
"""Inquire about the groupby variable, and create a text box to
collect input.
"""
if askyesno(
message="Would you like to specify a variable to group by?"
):
self.groupby_variable = askstring(
title="Select Group-by Variable",
prompt="Please enter the name/index of the group-by variable:",
)
try:
_validate_groupby_variable(
data=self.data, groupby_variable=self.groupby_variable
)
except GroupbyVariableError as error:
self.groupby_variable = None
showwarning(
title="Invalid Group-By Variable", message=error.message
)
else:
self.groupby_variable = None
def _get_graph_color(self) -> None:
"""Creates a graphical color picking tool to help set the desired
color for the generated graphs.
"""
color = askcolor(
color="cyan", title="Please select a color for the graphs"
)
# Pick the hexadecimal color format. `askcolor` returns a tuple e.g
# ((255.99609375, 69.26953125, 0.0), '#ff4500').
self.graph_color = color[-1] or "cyan"
def _get_save_as_name(self) -> None:
"""Create a file dialog to set destination of the generated report."""
save_name = asksaveasfilename(
initialdir=".",
initialfile="eda-report.docx",
filetypes=(("Word document", "*.docx"),),
title="Please select Save As file name",
)
self.save_name = save_name or "eda-report.docx"