Загрузка данных
import json
import os
import tkinter as tk
from tkinter import messagebox, ttk
CONFIG, LIMIT = "config.json", 50.0
class InventoryApp(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("780x560")
# 1. Чтение конфига
self.lang = "ru"
if os.path.exists(CONFIG):
try:
self.lang = json.load(open(CONFIG, encoding="utf-8")).get(
"language", "ru"
)
except Exception:
pass
else:
json.dump({"language": self.lang}, open(CONFIG, "w"))
# 2. Загрузка локалей и аудит эталонных ключей
self.locales, warnings = {}, []
for l in ("ru", "en"):
p = f"{l}.json"
self.locales[l] = (
json.load(open(p, encoding="utf-8")) if os.path.exists(p) else {}
)
ref_keys = set().union(*self.locales.values())
for l, d in self.locales.items():
for k in ref_keys - set(d.keys()):
warnings.append(
f"[LOCALIZATION] Отсутствует строка: {k} в файле {l}.json"
)
# 3. Данные инвентаря
self.items = [
{
"name": "Healing Potion",
"cat": "cat_potion",
"weight": 0.5,
"qty": 3,
},
{"name": "Iron Sword", "cat": "cat_weapon", "weight": 5.0, "qty": 1},
]
# 4. Интерфейс
self.cats = ["cat_weapon", "cat_armor", "cat_potion", "cat_misc"]
self.build_ui()
for w in warnings:
self.log(w)
self.apply_lang()
def t(self, key, **kwargs):
txt = self.locales.get(self.lang, {}).get(key, f"[MISSING: {key}]")
return txt.format(**kwargs) if kwargs and "{" in txt else txt
def log(self, text):
self.txt_log.config(state="normal")
self.txt_log.insert("end", text + "\n")
self.txt_log.see("end")
self.txt_log.config(state="disabled")
def build_ui(self):
# Меню
m = tk.Menu(self)
self.config(menu=m)
self.m_inv, self.m_char, self.m_lang = (
tk.Menu(m, tearoff=0),
tk.Menu(m, tearoff=0),
tk.Menu(m, tearoff=0),
)
for sub, name in (
(self.m_inv, "inv"),
(self.m_char, "char"),
(self.m_lang, "lang"),
):
m.add_cascade(menu=sub)
self.m_inv.add_command(command=self.quit)
self.m_char.add_command(
command=lambda: messagebox.showinfo(
self.t("menu_char_profile"), self.t("char_profile_info")
)
)
self.m_char.add_command(
command=lambda: (
self.items.clear(),
self.update_table(),
self.log(self.t("log_weight_normal")),
)
)
self.m_lang.add_command(
label="Русский (RU)", command=lambda: self.switch_lang("ru")
)
self.m_lang.add_command(
label="English (EN)", command=lambda: self.switch_lang("en")
)
# Форма добавления
f_in = ttk.Frame(self, padding=5)
f_in.pack(fill="x")
self.l_name = ttk.Label(f_in)
self.l_name.grid(row=0, column=0)
self.e_name = ttk.Entry(f_in, width=15)
self.e_name.grid(row=0, column=1, padx=4)
self.l_cat = ttk.Label(f_in)
self.l_cat.grid(row=0, column=2)
self.cb_cat = ttk.Combobox(f_in, state="readonly", width=12)
self.cb_cat.grid(row=0, column=3, padx=4)
self.l_w = ttk.Label(f_in)
self.l_w.grid(row=0, column=4)
self.e_w = ttk.Entry(f_in, width=8)
self.e_w.grid(row=0, column=5, padx=4)
self.l_q = ttk.Label(f_in)
self.l_q.grid(row=0, column=6)
self.e_q = ttk.Entry(f_in, width=6)
self.e_q.grid(row=0, column=7, padx=4)
self.b_add = ttk.Button(f_in, command=self.add_item)
self.b_add.grid(row=0, column=8, padx=6)
# Таблица
f_t = ttk.Frame(self)
f_t.pack(fill="both", expand=True, padx=5, pady=2)
self.tree = ttk.Treeview(
f_t,
columns=("n", "c", "w", "q", "tot"),
show="headings",
selectmode="browse",
)
self.tree.pack(side="left", fill="both", expand=True)
sb = ttk.Scrollbar(f_t, command=self.tree.yview)
sb.pack(side="right", fill="y")
self.tree.config(yscrollcommand=sb.set)
# Панель управления и статус
f_act = ttk.Frame(self, padding=5)
f_act.pack(fill="x")
self.b_use = ttk.Button(f_act, command=self.use_item)
self.b_use.pack(side="left", padx=2)
self.b_del = ttk.Button(f_act, command=self.del_item)
self.b_del.pack(side="left", padx=2)
self.l_stat = ttk.Label(f_act, font=("TkDefaultFont", 9, "bold"))
self.l_stat.pack(side="right", padx=5)
# Лог
self.f_log = ttk.LabelFrame(self, padding=3)
self.f_log.pack(fill="x", padx=5, pady=4)
self.txt_log = tk.Text(
self.f_log, height=4, state="disabled", wrap="word", bg="#f4f4f4"
)
self.txt_log.pack(fill="x")
def switch_lang(self, lang):
self.lang = lang
json.dump({"language": lang}, open(CONFIG, "w"))
self.apply_lang()
def apply_lang(self):
self.title(self.t("app_title"))
m = self.nametowidget(self.cget("menu"))
m.entryconfig(1, label=self.t("menu_inventory"))
m.entryconfig(2, label=self.t("menu_character"))
m.entryconfig(3, label=self.t("menu_language"))
self.m_inv.entryconfig(0, label=self.t("menu_exit"))
self.m_char.entryconfig(0, label=self.t("menu_char_profile"))
self.m_char.entryconfig(1, label=self.t("menu_char_reset"))
self.l_name.config(text=self.t("lbl_item_name"))
self.l_cat.config(text=self.t("lbl_category"))
self.l_w.config(text=self.t("lbl_weight"))
self.l_q.config(text=self.t("lbl_qty"))
self.b_add.config(text=self.t("btn_add"))
self.b_use.config(text=self.t("btn_use"))
self.b_del.config(text=self.t("btn_delete"))
self.f_log.config(text=self.t("log_title"))
ci = max(0, self.cb_cat.current())
self.cb_cat["values"] = [self.t(c) for c in self.cats]
self.cb_cat.current(ci if ci < len(self.cats) else 0)
for col, key in zip(
("n", "c", "w", "q", "tot"),
("col_name", "col_category", "col_weight", "col_qty", "col_total"),
):
self.tree.heading(col, text=self.t(key))
self.update_table()
def update_table(self):
self.tree.delete(*self.tree.get_children())
tot_w = 0.0
for it in self.items:
sub = it["weight"] * it["qty"]
tot_w += sub
self.tree.insert(
"",
"end",
values=(
it["name"],
self.t(it["cat"]),
f"{it['weight']:.1f}",
it["qty"],
f"{sub:.1f}",
),
)
self.l_stat.config(
text=self.t(
"stat_total_weight",
total=f"{tot_w:.1f}",
limit=f"{LIMIT:.1f}",
),
foreground="red" if tot_w > LIMIT else "black",
)
def add_item(self):
try:
name, w, q = (
self.e_name.get().strip(),
float(self.e_w.get()),
int(self.e_q.get()),
)
if not name or w < 0 or q <= 0:
raise ValueError
except ValueError:
return messagebox.showerror("!", self.t("err_invalid_input"))
cat = self.cats[self.cb_cat.current()]
self.items.append({"name": name, "cat": cat, "weight": w, "qty": q})
self.update_table()
self.log(self.t("log_item_added", name=name, qty=q))
if sum(i["weight"] * i["qty"] for i in self.items) > LIMIT:
self.log(self.t("log_overweight_warning"))
for e in (self.e_name, self.e_w, self.e_q):
e.delete(0, "end")
def use_item(self):
sel = self.tree.selection()
if not sel:
return
idx = self.tree.index(sel[0])
it = self.items[idx]
it["qty"] -= 1
self.log(self.t("log_item_used", name=it["name"]))
if it["qty"] <= 0:
self.items.pop(idx)
self.update_table()
def del_item(self):
sel = self.tree.selection()
if not sel:
return
idx = self.tree.index(sel[0])
it = self.items.pop(idx)
self.log(self.t("log_item_deleted", name=it["name"]))
self.update_table()
if __name__ == "__main__":
InventoryApp().mainloop()