This program takes x numbers and displays the mean, median and mode.
My goal here was to use classes and class properties the best I could to make the code shorter (honestly i may have just made it longer), so I would like to get some feedback on that.
import tkinter as tk
from typing import Callable, List
from statistics import mean, median, mode
"""MainFunctions = initialization creates 2 starting input boxes and the 3 outputs"""
"""Input = initialization creates an entry and stringvar for input"""
"""Output = initialization creates a label for the name of the average and another for the output"""
class Input:
def __init__(self, parent: tk.Tk, index: int, callback: Callable[[], None]) -> None:
self.value_var = tk.StringVar(parent)
self.value_var.trace_add(mode="write", callback=callback)
self.value_entry = tk.Entry(
parent, textvariable=self.value_var
)
self.value_entry.grid(row=index, column=1)
def delete_entry(self) -> None:
self.value_entry.destroy()
@property
def value(self) -> str:
if self.value_var.get():
return self.value_var.get()
else:
return None
@property
def row(self) -> int:
grid_info = self.value_entry.grid_info()
return grid_info['row']
class Output:
def __init__(self, parent: tk.Tk, index: int, name: str) -> None:
self.name = name
self.name_label = tk.Label(parent, text=f"{name}:")
self.name_label.grid(row=index, column=0)
self.output_label = tk.Label(parent, text=0)
self.output_label.grid(row=index, column=1)
def grid(self, row) -> None:
self.name_label.grid(row=row, column=0)
self.output_label.grid(row=row, column=1)
def set_output(self, values) -> None:
try:
match self.name:
case "Mean": self.output_label.configure(text=mean(values))
case "Median": self.output_label.configure(text=median(values))
case "Mode": self.output_label.configure(text=mode(values))
except:
self.output_label.configure(text=0)
class MainFunctions:
def __init__(self, parent: tk.Tk) -> None:
self.parent = parent
self.inputs = [
Input(self.parent, 0, self.input_callback),
Input(self.parent, 1, self.input_callback)
]
self.outputs = [
Output(
self.parent, 2, "Mean"
),
Output(
self.parent, 3, "Median"
),
Output(
self.parent, 4, "Mode"
)
]
def create_destroy_input(self) -> None:
"""Counts the number of empty stringvars"""
empty_stringvars = 0
for input in self.inputs[::-1]:
if input.value == None:
empty_stringvars += 1
"""Deletes all empty entries and stringvars except for 1, then creates another"""
for input in self.inputs[::-1]:
if empty_stringvars <= 1:
self.inputs += [
Input(
self.parent, len(self.inputs), self.input_callback
)
]
return
if input.value == None:
input.delete_entry()
self.inputs.remove(input)
empty_stringvars -= 1
def grid_output(self) -> None:
"""To grid the output according to the how many entries there are"""
row = 1
for output in self.outputs:
output.grid(row + len(self.inputs))
row += 1
def get_values(self) -> List:
"""Gets all values in entries"""
values = []
for input in self.inputs:
try:
value = input.value.replace(",", ".")
values.append(float(value))
except:
pass
return values
def set_output(self, values) -> None:
"""Calls function for setting output 3 times for each output label"""
for output in self.outputs:
output.set_output(values)
def input_callback(self, name: str, mode: str, index: str) -> None:
"""Every stringvar calls back to this"""
self.create_destroy_input()
self.grid_output()
self.set_output(self.get_values())
def main() -> None:
root = tk.Tk()
root.title("Mean, median and mode calculator")
root.resizable(width=False, height=False)
MainFunctions(root)
root.mainloop()
if __name__ == "__main__":
main()
1 Answer 1
GUI
When I run the code, the Tk window is too narrow, and I can't see full window title.
Also, it would be helpful to display some simple instructions for the user.
Input checking
When I enter a word instead of a number, it silently accepts it. Consider displaying some kind of message about invalid input.
Documentation
It is great that you have a docstring to describe each class, but it would be better to place them closer to their classes instead of at the top of the code.
Add a docstring to the top of the code to summarize the code's purpose:
"""
Calculate statistics for a list of numbers.
Launches a GUI window where the user can enter numbers.
"""
It is great that you added plenty of docstrings like:
"""Counts the number of empty stringvars"""
However, it is not clear what you mean by "stringvars". A brief explanation would be helpful.
Naming
Since input
is a Python keyword, it would be best to avoid using it as
a variable name in the MainFunctions
class:
for input in self.inputs[::-1]:
Perhaps name the variable as user_input
:
for user_input in self.inputs[::-1]:
The class name MainFunctions
is not very descriptive. Consider a name
which is more specific to what is does.
-
1
Explore related questions
See similar questions with these tags.