Files
Brovski-Adress-Etiketten-Ve…/src/brovski-adress-etiketten-verwaltung.py

389 lines
14 KiB
Python

import csv
import os
import sys
import tkinter as tk
from tkinter import font, filedialog
from configparser import NoSectionError, NoOptionError
from configparser import ConfigParser
from tkinter import messagebox
from tkinter import ttk
import json
class SettingsWindow(tk.Toplevel):
def __init__(self, root: tk.Tk):
super().__init__()
self.root = root
self.protocol("WM_DELETE_WINDOW", self.close_window)
self.geometry(f"500x330+{self.root.winfo_x() + 20}+{self.root.winfo_y() + 20}")
self.config = Config()
self.json_file = tk.StringVar()
self.csv_file = tk.StringVar()
title = font.Font(family='Ubuntu Mono', size=20, weight=font.BOLD)
tk.Label(self, text="Einstellungen", font=title).pack(pady=20)
path_frame = tk.Frame(self)
path_frame.pack(pady=(10, 40))
tk.Label(path_frame, text="Datenpfad JSON Datei").grid(row=0, column=0)
tk.Entry(path_frame, textvariable=self.json_file, width=50).grid(row=1, column=0)
tk.Button(path_frame, text="Pfad", command=self.set_json_path).grid(row=1, column=1)
tk.Label(path_frame, text="Datenpfad CSV Export Datei").grid(row=2, column=0)
tk.Entry(path_frame, textvariable=self.csv_file, width=50).grid(row=3, column=0)
tk.Button(path_frame, text="Pfad", command=self.set_csv_path).grid(row=3, column=1)
bottom_frame = tk.Frame(self)
bottom_frame.pack()
tk.Button(bottom_frame, text="Ok", command=self.ok).pack(side=tk.LEFT)
tk.Button(bottom_frame, text="Abbrechen", command=self.cancel).pack(side=tk.LEFT)
self.load_config()
def set_json_path(self):
self.json_file.set(filedialog.askdirectory(initialdir="~/", title="Datenpfad JSON Datei"))
def set_csv_path(self):
self.csv_file.set(filedialog.askdirectory(initialdir="~/", title="Datenpfad CSV Datei"))
def load_config(self):
try:
self.json_file.set(self.config.get("json", "path"))
except NoSectionError:
self.config.add_section("json")
self.config.set("json", "path", "")
except NoOptionError:
self.config.set("json", "path", "")
try:
self.csv_file.set(self.config.get("csv", "path"))
except NoSectionError:
self.config.add_section("csv")
self.config.set("csv", "path", "")
except NoOptionError:
self.config.set("csv", "path", "")
def ok(self):
self.config.set("json", "path", self.json_file.get())
self.config.set("csv", "path", self.csv_file.get())
self.close_window()
def cancel(self):
self.close_window()
def close_window(self):
if self.json_file.get() == "" or self.csv_file.get() == "":
messagebox.showwarning(title="Fehlerhafte Konfiguration", message="Pfad für JSON oder CSV Datei fehlt")
return
self.destroy_window()
def destroy_window(self):
self.root.update()
self.root.deiconify()
self.destroy()
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):
# tkinter settings
x_offset = 700
y_offset = 200
width = 1050
height = 1000
title = "Brovski Adress-Etiketten Verwaltung"
self.root = tk.Tk(className="BrovskiAdressEtiketten")
self.root.title(title)
self.root.protocol("WM_DELETE_WINDOW", self.on_close)
self.root.geometry(f"{width}x{height}+{x_offset}+{y_offset}")
self.config = Config()
self.config_good = False
# variables
self.address_list = []
self.current_record: int | None = None
self.sort_order = False
# init paths to json and csv file
self.json_file_name = "brovski-adress-etiketten-verwaltung.json"
self.csv_file_name = "brovski-adress-etiketten.csv"
self.json_path = ""
self.csv_path = ""
self.load_config()
self.json_file = os.path.join(self.json_path, self.json_file_name)
self.csv_file = os.path.join(self.csv_path, self.csv_file_name)
# leave application if settings are bad
if not self.config_good:
self.show_error("Fehler Konfiguration",
"Die Konfiguration ist fehlerhaft, bitte prüfe deine config.ini")
sys.exit()
top_frame = tk.Frame(self.root)
top_frame.pack(side=tk.TOP, fill=tk.X)
button_width = 8
tk.Button(top_frame, text="Insert", command=self.insert_record, width=button_width).pack(side=tk.LEFT)
tk.Button(top_frame, text="Delete", command=self.delete_record, width=button_width).pack(side=tk.LEFT)
tk.Button(top_frame, text="Export CSV", command=self.export_csv, width=button_width).pack(side=tk.LEFT)
tk.Button(top_frame, text="Quit", command=self.on_close, width=button_width).pack(side=tk.RIGHT)
tk.Button(top_frame, text="Settings", command=self.show_settings, width=button_width).pack(side=tk.RIGHT)
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)
scrollbar = ttk.Scrollbar(data_frame, orient=tk.VERTICAL)
self.table = ttk.Treeview(data_frame, yscrollcommand=scrollbar.set, columns=("0", "1", "2", "3", "4"),
show="headings")
scrollbar.config(command=self.table.yview)
self.table.heading('0', text="Aktiv")
self.table.column('0', anchor=tk.CENTER, width=0)
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(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.LEFT, fill=tk.Y)
self.table.bind("<ButtonRelease-1>", self.mouse_click)
self._load_json_file()
self.root.mainloop()
def load_config(self):
try:
self.json_path = self.config.get("json", "path")
self.csv_path = self.config.get("csv", "path")
self.config_good = True
except NoSectionError:
self.show_config_error()
self.show_settings()
except NoOptionError:
self.show_config_error()
self.show_settings()
def show_config_error(self):
if self.show_error("Fehlerhafte Konfiguration",
"Konnte benötigte Parameter in config.ini nicht finden"):
print("Fehlerhafte Konfiguration")
def on_close(self):
self.root.destroy()
def show_settings(self):
self.root.withdraw()
settings = SettingsWindow(self.root)
settings.wait_window()
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_json_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_json_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], reverse=self.sort_order)
self.populate_table()
case "#2":
self.address_list.sort(key=lambda x: x[int(column[-1]) - 1], reverse=self.sort_order)
self.populate_table()
case "#3":
self.address_list.sort(key=lambda x: x[int(column[-1]) - 1], reverse=self.sort_order)
self.populate_table()
case "#4":
self.address_list.sort(key=lambda x: x[int(column[-1]) - 1], reverse=self.sort_order)
self.populate_table()
case "#5":
self.address_list.sort(key=lambda x: x[int(column[-1]) - 1], reverse=self.sort_order)
self.populate_table()
case _:
print(column)
self.sort_order = not self.sort_order
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_json_file()
def _load_json_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="Datei nicht gefunden",
message=f"{self.json_file_name} nicht gefunden, erstelle leere Datei unter {self.json_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_json_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:
with open(self.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 {self.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()