import csv import os import tkinter as tk from configparser import NoOptionError from configparser import ConfigParser from tkinter import messagebox from tkinter import ttk import json class Config: parser: ConfigParser def __init__(self): """ Config parser reading config.ini Attributes: self.parser: ConfigParser holding list of sections and options self.__filename: Path and name to the config file """ self.parser = ConfigParser() self.config_file_name = "files/config.ini" self.root_path = os.path.dirname(os.path.abspath(__file__)) self.config_file = os.path.join(self.root_path, self.config_file_name) self._load() def _save(self): with open(self.config_file, 'w') as outfile: self.parser.write(outfile) def _load(self): self.parser.read(self.config_file) def add_section(self, section): self._load() self.parser.add_section(section) self._save() def set(self, section: str, option: str, value: str): self._load() self.parser.set(section, option, value) self._save() def get(self, section: str, option: str): self._load() return self.parser.get(section, option) class Application: def __init__(self): self.config = Config() self.address_list = [] self.current_record: int | None = None # json vars self.json_file_name = "files/address_data.json" self.root_path = os.path.dirname(os.path.abspath(__file__)) self.json_file = os.path.join(self.root_path, self.json_file_name) # tkinter settings x_offset = 700 y_offset = 400 title = "Brovski Adress-Etiketten Verwaltung" self.root = tk.Tk(className="BrovskiAdressEtiketten") self.root.title(title) self.root.geometry(f"+{x_offset}+{y_offset}") top_frame = tk.Frame(self.root) top_frame.pack(side=tk.TOP, fill=tk.X) button_width = 8 tk.Button(top_frame, text="Export CSV", command=self.export_csv, width=button_width).grid(row=0, column=2) tk.Button(top_frame, text="Insert", command=self.insert_record, width=button_width).grid(row=0, column=0) tk.Button(top_frame, text="Delete", command=self.delete_record, width=button_width).grid(row=0, column=1) self.aktiv = tk.StringVar() self.firma = tk.StringVar() self.name = tk.StringVar() self.strasse = tk.StringVar() self.plz_ort = tk.StringVar() edit_frame = tk.Frame(self.root) edit_frame.pack(side=tk.TOP, fill=tk.X) tk.Label(edit_frame, text="Aktiv").grid(row=0, column=0) tk.Label(edit_frame, text="Firma").grid(row=0, column=1) tk.Label(edit_frame, text="Name").grid(row=0, column=2) tk.Label(edit_frame, text="Strasse").grid(row=0, column=3) tk.Label(edit_frame, text="Plz/Ort").grid(row=0, column=4) edit_aktiv = tk.Checkbutton(edit_frame, variable=self.aktiv, onvalue="x", offvalue="") edit_aktiv.grid(row=1, column=0) edit_firma = tk.Entry(edit_frame, textvariable=self.firma) edit_firma.grid(row=1, column=1) edit_name = tk.Entry(edit_frame, textvariable=self.name) edit_name.grid(row=1, column=2) edit_strasse = tk.Entry(edit_frame, textvariable=self.strasse) edit_strasse.grid(row=1, column=3) edit_plz_ort = tk.Entry(edit_frame, textvariable=self.plz_ort) edit_plz_ort.grid(row=1, column=4) tk.Button(edit_frame, text="Update", command=self.update_record).grid(row=1, column=5) data_frame = tk.Frame(self.root, bg="teal") data_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True) self.table = ttk.Treeview(data_frame, columns=("0", "1", "2", "3", "4"), show="headings") self.table.heading('0', text="Aktiv") self.table.heading('1', text="Firma") self.table.heading('2', text="Name") self.table.heading('3', text="Strasse") self.table.heading('4', text="Plz/Ort") self.table.pack() self.table.bind("", self.mouse_click) self._load_file() self.root.mainloop() def insert_record(self): if self.current_record is not None: self.clear_entry_fields() children = self.table.get_children() values = [ "x", "Firma", "Name", "Strasse", "Plz/Ort", ] self.table.insert('', 'end', values=values) self._save_file() def delete_record(self): if self.current_record is None: return if messagebox.askyesno( "Eintrag löschen?", "Willst du diesen Eintrag wirklich löschen?\nDies kann nicht rückgängig gemacht werden"): self.table.delete(self.current_record) self.clear_entry_fields() self._save_file() def clear_entry_fields(self): entry_var_list = [self.aktiv, self.firma, self.name, self.strasse, self.plz_ort] for entry in entry_var_list: entry.set("") self.current_record = None def mouse_click(self, event): region = self.table.identify("region", event.x, event.y) if region == "heading": column = self.table.identify_column(event.x) match column: case "#1": self.address_list.sort(key=lambda x: x[int(column[-1]) - 1]) self.populate_table() case "#2": self.address_list.sort(key=lambda x: x[int(column[-1]) - 1]) self.populate_table() case "#3": self.address_list.sort(key=lambda x: x[int(column[-1]) - 1]) self.populate_table() case "#4": self.address_list.sort(key=lambda x: x[int(column[-1]) - 1]) self.populate_table() case "#5": self.address_list.sort(key=lambda x: x[int(column[-1]) - 1]) self.populate_table() case _: print(column) if region == "cell": self.current_record = self.table.focus() values = self.table.item(self.current_record, "values") entry_var_list = [self.aktiv, self.firma, self.name, self.strasse, self.plz_ort] for i in range(len(values)): entry_var_list[i].set(values[i]) def update_record(self): if self.current_record is None: return values = { 0: self.aktiv.get(), 1: self.firma.get(), 2: self.name.get(), 3: self.strasse.get(), 4: self.plz_ort.get(), } for key, value in values.items(): self.table.set(self.current_record, key, value) self.clear_entry_fields() self._save_file() def _load_file(self): try: with open(self.json_file, "r", encoding="utf-8") as f: self.address_list = json.load(f) self.address_list.sort(key=lambda x: (x[0], x[1])) except FileNotFoundError: self.show_error( message_title="File not found", message=f"{self.json_file_name} not found, creating empty file at {self.root_path}" ) self.address_list = [["firma", "name", "adresse", "plz/ort"]] with open(self.json_file, "w", encoding="utf-8") as f: json.dump(self.address_list, f) self.populate_table() def _save_file(self): self.export_table_to_address_list() try: with open(self.json_file, "w", encoding="utf-8") as f: json.dump(self.address_list, f, indent=4, sort_keys=True) except FileNotFoundError: self.show_error( message_title="Unexpected Error: File not found?!", message=f"{self.json_file_name} not found" ) def export_csv(self): try: file_name = self.config.get("csv", "file_name") except NoOptionError: self.show_error("Error: Option missing", "Option file_name is missing [csv]") return try: path = self.config.get("csv", "path") except NoOptionError: self.show_error("Error: Option missing", "Option path is missing [csv]") return if file_name == "": self.show_error("Error: Bad config file", "var file_name for section [csv] not set") return if path == "": self.show_error( "Alert: CSV path not set", f"var path for section [csv] not set, file will be saved in {self.root_path}" ) path = self.root_path csv_file = os.path.join(path, file_name) try: with open(csv_file, "w", encoding="utf-8") as f: writer = csv.writer(f, delimiter=",") for address in self.address_list: if address[0] != "x": continue del address[0] if address[0] == "": del address[0] writer.writerow(address) except FileNotFoundError: self.show_error("Unexpected error", f"Could not write file {csv_file}") def populate_table(self): self.delete_all_table_items() for index, item in enumerate(self.address_list): self.table.insert('', 'end', iid=index, values=item) def export_table_to_address_list(self): self.address_list.clear() for child in self.table.get_children(): self.address_list.append([]) for value in self.table.item(child)['values']: self.address_list[-1].append(value) def delete_all_table_items(self): for item in self.table.get_children(): self.table.delete(item) @staticmethod def show_error(message_title: str, message: str): messagebox.showwarning(title=message_title, message=message) if __name__ == '__main__': Application()