3
\$\begingroup\$

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()
toolic
14.7k5 gold badges29 silver badges204 bronze badges
asked Nov 29, 2021 at 16:17
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

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.

answered Nov 25, 2024 at 1:10
\$\endgroup\$
1
  • 1
    \$\begingroup\$ For shadowing input(), PEP 8 recommends "single_trailing_underscore_: used by convention to avoid conflicts with Python keyword", which would give us input_. \$\endgroup\$ Commented Nov 25, 2024 at 5:12

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.