Translations, suggestion box, UX
All checks were successful
Gitea/kapitanbooru-uploader/pipeline/head This commit looks good
All checks were successful
Gitea/kapitanbooru-uploader/pipeline/head This commit looks good
This commit is contained in:
@@ -11,8 +11,9 @@ from typing import Dict, Tuple, Optional
|
||||
import networkx as nx
|
||||
import requests
|
||||
from PIL import Image, ImageTk, PngImagePlugin
|
||||
|
||||
import wdtagger as wdt
|
||||
|
||||
from .I18N import _
|
||||
from .ProgressFile import ProgressFile
|
||||
from .TagsRepo import TagsRepo
|
||||
from .autocomplete import TagManager
|
||||
@@ -26,11 +27,11 @@ class ProcessingDialog:
|
||||
def __init__(self, root, target_function, *args):
|
||||
self.root = root
|
||||
self.top = tk.Toplevel(root)
|
||||
self.top.title("Processing...")
|
||||
self.top.title(_("Processing..."))
|
||||
self.top.geometry("300x150")
|
||||
self.top.protocol("WM_DELETE_WINDOW", self.on_close)
|
||||
|
||||
self.label = tk.Label(self.top, text="Processing, please wait...")
|
||||
self.label = tk.Label(self.top, text=_("Processing, please wait..."))
|
||||
self.label.pack(pady=10)
|
||||
|
||||
# Start with indeterminate progress bar
|
||||
@@ -38,8 +39,15 @@ class ProcessingDialog:
|
||||
self.progress.pack(pady=10, fill="x")
|
||||
self.progress.start(10)
|
||||
|
||||
sig = inspect.signature(target_function)
|
||||
if "secondary_progress_queue" in sig.parameters:
|
||||
self.sub_progress = ttk.Progressbar(self.top, mode="indeterminate")
|
||||
self.sub_progress.pack(pady=10, fill="x")
|
||||
self.sub_progress.start(10)
|
||||
|
||||
# Setup communication queue and periodic checker
|
||||
self.queue = queue.Queue()
|
||||
self.sub_queue = queue.Queue()
|
||||
self.running = True
|
||||
self.cancel_event = threading.Event() # Cancellation flag
|
||||
self.thread = threading.Thread(
|
||||
@@ -58,6 +66,10 @@ class ProcessingDialog:
|
||||
self.progress.config(mode=msg[1])
|
||||
if msg[1] == "determinate":
|
||||
self.progress["value"] = 0
|
||||
self.progress.stop()
|
||||
elif msg[1] == "indeterminate":
|
||||
self.progress["value"] = 0
|
||||
self.progress.start()
|
||||
elif msg[0] == "max":
|
||||
self.progress["maximum"] = msg[1]
|
||||
elif msg[0] == "progress":
|
||||
@@ -69,6 +81,26 @@ class ProcessingDialog:
|
||||
except queue.Empty:
|
||||
break
|
||||
|
||||
try:
|
||||
msg = self.sub_queue.get_nowait()
|
||||
|
||||
if msg[0] == "mode":
|
||||
self.sub_progress.config(mode=msg[1])
|
||||
if msg[1] == "determinate":
|
||||
self.sub_progress["value"] = 0
|
||||
self.sub_progress.stop()
|
||||
elif msg[1] == "indeterminate":
|
||||
self.sub_progress["value"] = 0
|
||||
self.sub_progress.start()
|
||||
elif msg[0] == "max":
|
||||
self.sub_progress["maximum"] = msg[1]
|
||||
elif msg[0] == "progress":
|
||||
self.sub_progress["value"] = msg[1]
|
||||
|
||||
self.top.update_idletasks()
|
||||
except queue.Empty:
|
||||
break
|
||||
|
||||
if self.running:
|
||||
self.top.after(100, self.process_queue)
|
||||
|
||||
@@ -79,6 +111,8 @@ class ProcessingDialog:
|
||||
kwargs = {}
|
||||
if "progress_queue" in sig.parameters:
|
||||
kwargs["progress_queue"] = self.queue
|
||||
if "secondary_progress_queue" in sig.parameters:
|
||||
kwargs["secondary_progress_queue"] = self.sub_queue
|
||||
if "cancel_event" in sig.parameters:
|
||||
kwargs["cancel_event"] = self.cancel_event
|
||||
target_function(*args, **kwargs)
|
||||
@@ -91,7 +125,7 @@ class ProcessingDialog:
|
||||
self.running = False
|
||||
self.progress.stop()
|
||||
self.top.after(0, self.top.destroy)
|
||||
self.thread.join()
|
||||
self.root.after(100, self.thread.join)
|
||||
|
||||
def on_close(self):
|
||||
"""Handle manual window closure"""
|
||||
@@ -105,6 +139,7 @@ class ImageBrowser(tk.Tk):
|
||||
super().__init__()
|
||||
self.title("Kapitanbooru Uploader")
|
||||
self.geometry("900x600")
|
||||
self.version = "0.4.0"
|
||||
|
||||
self.settings = Settings()
|
||||
self.tags_repo = TagsRepo(self.settings)
|
||||
@@ -169,6 +204,15 @@ class ImageBrowser(tk.Tk):
|
||||
self.create_widgets()
|
||||
self.bind_events()
|
||||
|
||||
def reload_ui(self):
|
||||
"""Reload UI components with new language"""
|
||||
# Destroy current widgets
|
||||
for widget in self.winfo_children():
|
||||
widget.destroy()
|
||||
# Rebuild UI
|
||||
self.create_menu()
|
||||
self.create_widgets()
|
||||
|
||||
def load_implication_graph(self) -> nx.DiGraph:
|
||||
G = nx.DiGraph()
|
||||
conn = self.tags_repo.get_conn()
|
||||
@@ -249,7 +293,7 @@ class ImageBrowser(tk.Tk):
|
||||
png_tags = set(parse_parameters(parameters, self.tags_repo).split())
|
||||
img.close()
|
||||
except Exception as e:
|
||||
print("Błąd przy otwieraniu pliku", file_path, ":", e)
|
||||
print(_("Błąd przy otwieraniu pliku"), file_path, ":", e)
|
||||
png_tags = set()
|
||||
|
||||
# Pobierz tagi z Taggera – sprawdzając cache
|
||||
@@ -291,10 +335,10 @@ class ImageBrowser(tk.Tk):
|
||||
self.tagger_cache[md5] = result
|
||||
self.tagger_processed.add(md5)
|
||||
self.after(0, self.update_status_bar)
|
||||
print(f"Tagger przetworzył: {file_path}")
|
||||
print(_("Tagger przetworzył:"), f"{file_path}")
|
||||
return result
|
||||
except Exception as e:
|
||||
print("Błąd Taggera dla", file_path, ":", e)
|
||||
print(_("Błąd Taggera dla"), file_path, ":", e)
|
||||
|
||||
def map_tagger_rating(self, result: wdt.Result) -> str:
|
||||
"""
|
||||
@@ -320,37 +364,98 @@ class ImageBrowser(tk.Tk):
|
||||
self.file_menu = tk.Menu(menubar, tearoff=0)
|
||||
|
||||
# File menu items - create references for items we need to control
|
||||
self.file_menu.add_command(label="Otwórz folder", command=self.select_folder)
|
||||
self.file_menu.add_command(label=_("Otwórz folder"), command=self.select_folder)
|
||||
self.file_menu.add_separator()
|
||||
self.file_menu.add_command(
|
||||
label="Wyślij", command=self.upload_current_image, state=tk.DISABLED
|
||||
label=_("Wyślij"), command=self.upload_current_image, state=tk.DISABLED
|
||||
)
|
||||
self.file_menu.add_command(
|
||||
label="Wyślij wszystko", command=self.upload_all_files, state=tk.DISABLED
|
||||
label=_("Wyślij wszystko"), command=self.upload_all_files, state=tk.DISABLED
|
||||
)
|
||||
self.file_menu.add_separator()
|
||||
self.file_menu.add_command(
|
||||
label="Podmień tagi", command=self.edit_current_image, state=tk.DISABLED
|
||||
label=_("Podmień tagi"), command=self.edit_current_image, state=tk.DISABLED
|
||||
)
|
||||
self.file_menu.add_command(
|
||||
label="Otwórz post", command=self.view_current_post, state=tk.DISABLED
|
||||
label=_("Otwórz post"), command=self.view_current_post, state=tk.DISABLED
|
||||
)
|
||||
self.file_menu.add_separator()
|
||||
self.file_menu.add_command(label="Zakończ", command=self.quit)
|
||||
self.file_menu.add_command(label=_("Zakończ"), command=self.quit)
|
||||
|
||||
menubar.add_cascade(label="Plik", menu=self.file_menu)
|
||||
menubar.add_cascade(label=_("Plik"), menu=self.file_menu)
|
||||
|
||||
# Options menu
|
||||
options_menu = tk.Menu(menubar, tearoff=0)
|
||||
options_menu.add_command(label="Ustawienia", command=self.open_settings)
|
||||
options_menu.add_command(label=_("Ustawienia"), command=self.open_settings)
|
||||
options_menu.add_separator()
|
||||
options_menu.add_command(
|
||||
label="Wyczyść cache Taggera", command=self.clear_cache
|
||||
label=_("Wyczyść cache Taggera"), command=self.clear_cache
|
||||
)
|
||||
options_menu.add_command(
|
||||
label="Zregeneruj bazę tagów", command=self.regenerate_tags_db
|
||||
label=_("Zregeneruj bazę tagów"), command=self.regenerate_tags_db
|
||||
)
|
||||
menubar.add_cascade(label="Opcje", menu=options_menu)
|
||||
menubar.add_cascade(label=_("Opcje"), menu=options_menu)
|
||||
|
||||
help_menu = tk.Menu(menubar, tearoff=0)
|
||||
help_menu.add_command(label=_("About"), command=self.show_about)
|
||||
menubar.add_cascade(label=_("Help"), menu=help_menu)
|
||||
|
||||
def show_about(self):
|
||||
"""Multilingual About window (updated version)"""
|
||||
about = tk.Toplevel(self)
|
||||
about.title(_("About Kapitanbooru Uploader"))
|
||||
about.resizable(False, False)
|
||||
|
||||
# Window content
|
||||
frame = ttk.Frame(about, padding=10)
|
||||
frame.pack(fill="both", expand=True)
|
||||
|
||||
ttk.Label(
|
||||
frame, text="Kapitanbooru Uploader", font=("TkDefaultFont", 16, "bold")
|
||||
).grid(row=0, column=0, sticky=tk.W)
|
||||
|
||||
content = [
|
||||
(f"Version {self.version}", 1),
|
||||
("", 2),
|
||||
(_("A GUI application for uploading images to KapitanBooru."), 3),
|
||||
(_("Features include image upload, tag management, automatic"), 4),
|
||||
(_("tagging with wdtagger, and cache management."), 5),
|
||||
("", 6),
|
||||
(_("Authors:"), 7),
|
||||
("Michał Leśniak", 8),
|
||||
("", 9),
|
||||
(_("License: MIT License"), 10),
|
||||
(f"Copyright © 2025 Michał Leśniak", 11),
|
||||
("", 12),
|
||||
]
|
||||
|
||||
for text, row in content:
|
||||
ttk.Label(frame, text=text).grid(row=row, column=0, sticky=tk.W)
|
||||
|
||||
# Repository link
|
||||
repo_frame = ttk.Frame(frame)
|
||||
repo_frame.grid(row=13, sticky=tk.W)
|
||||
ttk.Label(repo_frame, text=_("Repository:")).pack(side=tk.LEFT)
|
||||
repo_url = "https://git.mlesniak.pl/kapitan/kapitanbooru-uploader"
|
||||
repo = ttk.Label(repo_frame, text=repo_url, cursor="hand2", foreground="blue")
|
||||
repo.pack(side=tk.LEFT, padx=(5, 0))
|
||||
repo.bind("<Button-1>", lambda e: open_webbrowser(repo_url, self.settings))
|
||||
|
||||
# Website link
|
||||
website_frame = ttk.Frame(frame)
|
||||
website_frame.grid(row=14, sticky=tk.W)
|
||||
ttk.Label(website_frame, text=_("Website:")).pack(side=tk.LEFT)
|
||||
website_url = "https://booru.mlesniak.pl"
|
||||
website = ttk.Label(
|
||||
website_frame, text=website_url, cursor="hand2", foreground="blue"
|
||||
)
|
||||
website.pack(side=tk.LEFT, padx=(5, 0))
|
||||
website.bind(
|
||||
"<Button-1>", lambda e: open_webbrowser(website_url, self.settings)
|
||||
)
|
||||
|
||||
# Close button
|
||||
ttk.Button(about, text=_("Close"), command=about.destroy).pack(pady=10)
|
||||
|
||||
def regenerate_tags_db(self):
|
||||
self.processing_dialog = ProcessingDialog(self, self.tags_repo.regenerate_db)
|
||||
@@ -358,41 +463,44 @@ class ImageBrowser(tk.Tk):
|
||||
def clear_cache(self):
|
||||
res, err = self.tagger_cache.clear_cache()
|
||||
if res:
|
||||
messagebox.showinfo("Cache", "Cache Taggera zostało wyczyszczone.")
|
||||
messagebox.showinfo(_("Cache"), _("Cache Taggera zostało wyczyszczone."))
|
||||
else:
|
||||
messagebox.showerror("Cache", f"Błąd przy czyszczeniu cache: {err}")
|
||||
messagebox.showerror(
|
||||
_("Cache"), f"{_('Błąd przy czyszczeniu cache:')} {err}"
|
||||
)
|
||||
|
||||
def open_settings(self):
|
||||
settings_window = tk.Toplevel(self)
|
||||
settings_window.title("Ustawienia")
|
||||
settings_window.geometry("300x350")
|
||||
settings_window.title(_("Ustawienia"))
|
||||
settings_window.geometry("300x430") # Enlarged vertically
|
||||
settings_window.resizable(False, False) # Disable resizing
|
||||
settings_window.grab_set()
|
||||
|
||||
lbl_login = tk.Label(settings_window, text="Login:")
|
||||
lbl_login = tk.Label(settings_window, text=_("Login:"))
|
||||
lbl_login.pack(pady=(10, 0))
|
||||
entry_login = tk.Entry(settings_window)
|
||||
entry_login.pack(pady=(0, 10), padx=10, fill="x")
|
||||
entry_login.insert(0, self.settings.username)
|
||||
|
||||
lbl_password = tk.Label(settings_window, text="Hasło:")
|
||||
lbl_password = tk.Label(settings_window, text=_("Hasło:"))
|
||||
lbl_password.pack(pady=(10, 0))
|
||||
entry_password = tk.Entry(settings_window, show="*")
|
||||
entry_password.pack(pady=(0, 10), padx=10, fill="x")
|
||||
entry_password.insert(0, self.settings.password)
|
||||
|
||||
lbl_base_url = tk.Label(settings_window, text="Base URL:")
|
||||
lbl_base_url = tk.Label(settings_window, text=_("Base URL:"))
|
||||
lbl_base_url.pack(pady=(10, 0))
|
||||
entry_base_url = tk.Entry(settings_window)
|
||||
entry_base_url.pack(pady=(0, 10), padx=10, fill="x")
|
||||
entry_base_url.insert(0, self.settings.base_url)
|
||||
|
||||
lbl_default_tags = tk.Label(settings_window, text="Default Tags:")
|
||||
lbl_default_tags = tk.Label(settings_window, text=_("Default Tags:"))
|
||||
lbl_default_tags.pack(pady=(10, 0))
|
||||
entry_default_tags = tk.Entry(settings_window)
|
||||
entry_default_tags.pack(pady=(0, 10), padx=10, fill="x")
|
||||
entry_default_tags.insert(0, self.settings.default_tags)
|
||||
|
||||
lbl_browser = tk.Label(settings_window, text="Browser:")
|
||||
lbl_browser = tk.Label(settings_window, text=_("Browser:"))
|
||||
lbl_browser.pack(pady=(10, 0))
|
||||
cb_browser = ttk.Combobox(
|
||||
settings_window,
|
||||
@@ -406,16 +514,39 @@ class ImageBrowser(tk.Tk):
|
||||
)
|
||||
)
|
||||
|
||||
lbl_lang = tk.Label(settings_window, text=_("Language:"))
|
||||
lbl_lang.pack(pady=(10, 0))
|
||||
cb_lang = ttk.Combobox(
|
||||
settings_window,
|
||||
values=list(self.settings.i18n.languages.values()),
|
||||
state="readonly",
|
||||
)
|
||||
cb_lang.pack(pady=(0, 10), padx=10, fill="x")
|
||||
cb_lang.set(
|
||||
self.settings.i18n.languages.get(self.settings.i18n.current_lang, "en")
|
||||
)
|
||||
|
||||
def save_and_close():
|
||||
self.settings.username = entry_login.get()
|
||||
self.settings.password = entry_password.get()
|
||||
self.settings.base_url = entry_base_url.get()
|
||||
self.settings.default_tags = entry_default_tags.get()
|
||||
self.settings.browser = self.settings.installed_browsers[cb_browser.get()]
|
||||
self.settings.i18n.set_language(
|
||||
next(
|
||||
(
|
||||
lang
|
||||
for lang, name in self.settings.i18n.languages.items()
|
||||
if name == cb_lang.get()
|
||||
),
|
||||
"en",
|
||||
)
|
||||
)
|
||||
self.settings.save_settings()
|
||||
settings_window.destroy()
|
||||
self.reload_ui()
|
||||
|
||||
btn_save = tk.Button(settings_window, text="Zapisz", command=save_and_close)
|
||||
btn_save = tk.Button(settings_window, text=_("Zapisz"), command=save_and_close)
|
||||
btn_save.pack(pady=10)
|
||||
|
||||
def create_widgets(self):
|
||||
@@ -457,7 +588,7 @@ class ImageBrowser(tk.Tk):
|
||||
right_frame.grid_rowconfigure(4, weight=0) # Upload Panel
|
||||
|
||||
# PNG Tags – widget Text z scrollbar
|
||||
png_frame = tk.LabelFrame(right_frame, text="PNG Tags")
|
||||
png_frame = tk.LabelFrame(right_frame, text=_("PNG Tags"))
|
||||
png_frame.grid(row=0, column=0, sticky=tk.EW, padx=5, pady=5)
|
||||
png_frame.grid_columnconfigure(0, weight=1)
|
||||
self.png_tags_text = tk.Text(png_frame, wrap=tk.WORD)
|
||||
@@ -469,7 +600,7 @@ class ImageBrowser(tk.Tk):
|
||||
)
|
||||
|
||||
# Tagger Tags – widget Text z scrollbar
|
||||
tagger_frame = tk.LabelFrame(right_frame, text="Tagger Tags")
|
||||
tagger_frame = tk.LabelFrame(right_frame, text=_("Tagger Tags"))
|
||||
tagger_frame.grid(row=1, column=0, sticky=tk.EW, padx=5, pady=5)
|
||||
tagger_frame.grid_columnconfigure(0, weight=1)
|
||||
self.tagger_tags_text = tk.Text(tagger_frame, wrap=tk.WORD)
|
||||
@@ -483,7 +614,7 @@ class ImageBrowser(tk.Tk):
|
||||
)
|
||||
|
||||
# Manual Tags – Entry (stała wysokość)
|
||||
manual_frame = tk.LabelFrame(right_frame, text="Manual Tags")
|
||||
manual_frame = tk.LabelFrame(right_frame, text=_("Manual Tags"))
|
||||
manual_frame.grid(row=2, column=0, sticky=tk.NSEW, padx=5, pady=5)
|
||||
self.manual_tags_manager = TagManager(
|
||||
manual_frame, self.settings, self.tags_repo, self.update_final_tags
|
||||
@@ -491,7 +622,7 @@ class ImageBrowser(tk.Tk):
|
||||
self.manual_tags_manager.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# Final Tags – widget Text z scrollbar, który rozszerza się
|
||||
final_frame = tk.LabelFrame(right_frame, text="Final Tags")
|
||||
final_frame = tk.LabelFrame(right_frame, text=_("Final Tags"))
|
||||
final_frame.grid(row=3, column=0, sticky=tk.NSEW, padx=5, pady=5)
|
||||
final_frame.grid_rowconfigure(0, weight=1)
|
||||
final_frame.grid_columnconfigure(0, weight=1)
|
||||
@@ -511,12 +642,12 @@ class ImageBrowser(tk.Tk):
|
||||
)
|
||||
self.rating_dropdown.pack(side=tk.LEFT, padx=5)
|
||||
self.upload_button = tk.Button(
|
||||
upload_frame, text="Upload", command=self.upload_current_image
|
||||
upload_frame, text=_("Wyślij"), command=self.upload_current_image
|
||||
)
|
||||
self.upload_button.pack(side=tk.LEFT, padx=5)
|
||||
self.upload_button.config(state=tk.DISABLED)
|
||||
self.view_post_button = tk.Button(
|
||||
upload_frame, text="Wyświetl", command=self.view_current_post
|
||||
upload_frame, text=_("Wyświetl"), command=self.view_current_post
|
||||
)
|
||||
self.view_post_button.pack(side=tk.LEFT, padx=5)
|
||||
self.view_post_button.config(state=tk.DISABLED)
|
||||
@@ -533,9 +664,9 @@ class ImageBrowser(tk.Tk):
|
||||
|
||||
def update_status_bar(self):
|
||||
status_text = (
|
||||
f"Przetworzono tagi: {len(self.tagger_processed)}/{self.total_files} plików | "
|
||||
f"Zweryfikowano status uploadu: {self.upload_verified}/{self.total_files} plików | "
|
||||
f"Zuploadowano: {self.uploaded_count}/{self.upload_verified} plików"
|
||||
f"{_('Przetworzono tagi:')} {len(self.tagger_processed)}/{self.total_files} {_('plików')} | "
|
||||
f"{_('Zweryfikowano status uploadu:')} {self.upload_verified}/{self.total_files} {_('plików')} | "
|
||||
f"{_('Zuploadowano:')} {self.uploaded_count}/{self.upload_verified} {_('plików')}"
|
||||
)
|
||||
self.status_label.config(text=status_text)
|
||||
|
||||
@@ -563,7 +694,7 @@ class ImageBrowser(tk.Tk):
|
||||
Otwiera okno dialogowe wyboru folderu z obrazkami
|
||||
i wczytuje pliki PNG z wybranego folderu.
|
||||
"""
|
||||
folder = filedialog.askdirectory(title="Wybierz folder z obrazkami PNG")
|
||||
folder = filedialog.askdirectory(title=_("Wybierz folder z obrazkami PNG"))
|
||||
if folder:
|
||||
self.folder_path = folder
|
||||
self.load_images()
|
||||
@@ -593,7 +724,9 @@ class ImageBrowser(tk.Tk):
|
||||
self.show_image(0)
|
||||
self.post_load_processing()
|
||||
else:
|
||||
messagebox.showinfo("Informacja", "Brak plików PNG w wybranym folderze.")
|
||||
messagebox.showinfo(
|
||||
_("Informacja"), _("Brak plików PNG w wybranym folderze.")
|
||||
)
|
||||
|
||||
def post_load_processing(self):
|
||||
"""
|
||||
@@ -675,7 +808,7 @@ class ImageBrowser(tk.Tk):
|
||||
self.uploaded[file_path] = False
|
||||
self.after(0, self.update_status_bar)
|
||||
except Exception as e:
|
||||
print("Błąd podczas sprawdzania paczki uploadu:", e)
|
||||
print(_("Błąd podczas sprawdzania paczki uploadu:"), e)
|
||||
self.after(100, self.join_check_uploaded_files_thread)
|
||||
|
||||
def update_button_states(self):
|
||||
@@ -716,14 +849,14 @@ class ImageBrowser(tk.Tk):
|
||||
post_ops_state = tk.NORMAL if post_id else tk.DISABLED
|
||||
|
||||
# Update menu items
|
||||
self.file_menu.entryconfig("Wyślij wszystko", state=send_all_state)
|
||||
self.file_menu.entryconfig("Podmień tagi", state=post_ops_state)
|
||||
self.file_menu.entryconfig("Otwórz post", state=post_ops_state)
|
||||
self.file_menu.entryconfig(_("Wyślij wszystko"), state=send_all_state)
|
||||
self.file_menu.entryconfig(_("Podmień tagi"), state=post_ops_state)
|
||||
self.file_menu.entryconfig(_("Otwórz post"), state=post_ops_state)
|
||||
|
||||
# Update buttons
|
||||
self.upload_button.config(
|
||||
state=tk.NORMAL if has_current else tk.DISABLED,
|
||||
text="Podmień tagi" if post_id else "Wyślij",
|
||||
text=_("Podmień tagi") if post_id else _("Wyślij"),
|
||||
command=self.edit_current_image if post_id else self.upload_current_image,
|
||||
)
|
||||
self.view_post_button.config(state=post_ops_state)
|
||||
@@ -731,7 +864,7 @@ class ImageBrowser(tk.Tk):
|
||||
# Special case for "Wyślij" menu item
|
||||
wyślij_state = tk.DISABLED if post_id else tk.NORMAL
|
||||
self.file_menu.entryconfig(
|
||||
"Wyślij", state=wyślij_state if has_current else tk.DISABLED
|
||||
_("Wyślij"), state=wyślij_state if has_current else tk.DISABLED
|
||||
)
|
||||
|
||||
def view_current_post(self):
|
||||
@@ -751,7 +884,7 @@ class ImageBrowser(tk.Tk):
|
||||
for chunk in iter(lambda: f.read(chunk_size), b""):
|
||||
hash_md5.update(chunk)
|
||||
except Exception as e:
|
||||
print("Błąd przy obliczaniu MD5:", e)
|
||||
print(_("Błąd przy obliczaniu MD5:"), e)
|
||||
return ""
|
||||
return hash_md5.hexdigest()
|
||||
|
||||
@@ -807,7 +940,7 @@ class ImageBrowser(tk.Tk):
|
||||
self.tagger_thread_idx += 1
|
||||
thread.start()
|
||||
except Exception as e:
|
||||
messagebox.showerror("Błąd", f"Nie można załadować obrazka:\n{e}")
|
||||
messagebox.showerror(_("Błąd"), f"{_('Nie można załadować obrazka:')}\n{e}")
|
||||
|
||||
def edit_current_image(self):
|
||||
"""
|
||||
@@ -1016,13 +1149,21 @@ class ImageBrowser(tk.Tk):
|
||||
if tag in self.implication_graph:
|
||||
implied_by_selected.update(nx.descendants(self.implication_graph, tag))
|
||||
else:
|
||||
print(f"Warning: Tag '{tag}' not found in implication graph")
|
||||
print(
|
||||
_("Warning: Tag '{tag}' not found in implication graph").format(
|
||||
tag=tag
|
||||
)
|
||||
)
|
||||
self.missing_tags.add(tag) # Log missing tags
|
||||
for tag in selected_png_tags:
|
||||
if tag in self.implication_graph:
|
||||
implied_by_selected.update(nx.descendants(self.implication_graph, tag))
|
||||
else:
|
||||
print(f"Warning: Tag '{tag}' not found in implication graph")
|
||||
print(
|
||||
_("Warning: Tag '{tag}' not found in implication graph").format(
|
||||
tag=tag
|
||||
)
|
||||
)
|
||||
self.missing_tags.add(tag) # Log missing tags
|
||||
|
||||
# Build visible list
|
||||
@@ -1249,7 +1390,7 @@ class ImageBrowser(tk.Tk):
|
||||
# Ustaw komunikat, że Tagger pracuje
|
||||
self.tagger_tags_text.config(state=tk.NORMAL)
|
||||
self.tagger_tags_text.delete("1.0", tk.END)
|
||||
self.tagger_tags_text.insert("1.0", "Tagger przetwarza...")
|
||||
self.tagger_tags_text.insert("1.0", _("Tagger przetwarza..."))
|
||||
self.tagger_tags_text.config(state=tk.DISABLED)
|
||||
file_path = self.image_files[self.current_index]
|
||||
result = self.get_tagger_results(file_path)
|
||||
@@ -1264,14 +1405,21 @@ class ImageBrowser(tk.Tk):
|
||||
file_path,
|
||||
final_tags=None,
|
||||
final_rating=None,
|
||||
progress_queue=None,
|
||||
cancel_event=None,
|
||||
progress_queue: Optional[queue.Queue] = None,
|
||||
cancel_event: Optional[threading.Event] = None,
|
||||
):
|
||||
base_file_name = os.path.basename(file_path)
|
||||
if progress_queue:
|
||||
progress_queue.put(("mode", "determinate"))
|
||||
progress_queue.put(("max", 100))
|
||||
progress_queue.put(("label", f"Wysyłam plik {base_file_name}..."))
|
||||
progress_queue.put(
|
||||
(
|
||||
"label",
|
||||
_("Wysyłam plik {base_file_name}...").format(
|
||||
base_file_name=base_file_name
|
||||
),
|
||||
)
|
||||
)
|
||||
url = self.settings.base_url.rstrip("/") + "/api/danbooru/add_post"
|
||||
tags = (
|
||||
self.final_tags_text.get("1.0", tk.END).strip()
|
||||
@@ -1308,22 +1456,29 @@ class ImageBrowser(tk.Tk):
|
||||
show_warn = False
|
||||
post_url = None
|
||||
if response.status_code in (200, 201):
|
||||
message = "Upload zakończony powodzeniem!"
|
||||
message = _("Wysyłanie zakończone powodzeniem!")
|
||||
post_url = response.headers.get("X-Danbooru-Location", None)
|
||||
elif response.status_code == 409:
|
||||
message = f"Upload zakończony błędem.\nStatus: 409\nTreść: {response.headers.get('X-Danbooru-Errors', '')}"
|
||||
message = _(
|
||||
"Wysyłanie zakończone błędem.\nStatus: {status_code}\nTreść: {text}"
|
||||
).format(
|
||||
status_code=response.status_code,
|
||||
text=response.headers.get("X-Danbooru-Errors", ""),
|
||||
)
|
||||
post_url = response.headers.get("X-Danbooru-Location", None)
|
||||
show_warn = True
|
||||
else:
|
||||
message = f"Upload zakończony błędem.\nStatus: {response.status_code}\nTreść: {response.text}"
|
||||
message = _(
|
||||
"Wysyłanie zakończone błędem.\nStatus: {status_code}\nTreść: {text}"
|
||||
).format(status_code=response.status_code, text=response.text)
|
||||
show_warn = True
|
||||
# Aktualizacja wyglądu listy – musimy użyć domyślnych argumentów w lambdzie, aby zachować bieżący indeks
|
||||
if show_warn:
|
||||
if not final_tags:
|
||||
messagebox.showwarning("Upload", message)
|
||||
messagebox.showwarning(_("Wysyłanie"), message)
|
||||
else:
|
||||
if not final_tags:
|
||||
messagebox.showinfo("Upload", message)
|
||||
messagebox.showinfo(_("Wysyłanie"), message)
|
||||
self.after(
|
||||
0,
|
||||
lambda idx=self.image_files.index(
|
||||
@@ -1336,7 +1491,7 @@ class ImageBrowser(tk.Tk):
|
||||
self.uploaded_count += 1
|
||||
self.after(0, self.update_status_bar)
|
||||
except Exception as e:
|
||||
messagebox.showerror("Błąd uploadu", str(e))
|
||||
messagebox.showerror(_("Błąd wysyłania"), str(e))
|
||||
finally:
|
||||
self.upload_button.after(0, self.update_button_states)
|
||||
|
||||
@@ -1356,20 +1511,27 @@ class ImageBrowser(tk.Tk):
|
||||
|
||||
if not post_id:
|
||||
messagebox.showerror(
|
||||
"Błąd edycji", "Post nie został znaleziony dla tego pliku"
|
||||
_("Błąd edycji"), _("Post nie został znaleziony dla tego pliku")
|
||||
)
|
||||
return
|
||||
|
||||
if progress_queue:
|
||||
progress_queue.put(("mode", "determinate"))
|
||||
progress_queue.put(("max", 100))
|
||||
progress_queue.put(("label", f"Aktualizuję tagi dla {base_file_name}..."))
|
||||
progress_queue.put(
|
||||
(
|
||||
"label",
|
||||
_("Aktualizuję tagi dla {base_file_name}...").format(
|
||||
base_file_name=base_file_name
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
# Check for cancellation before starting the operation.
|
||||
if cancel_event is not None and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(("label", "Operacja anulowana"))
|
||||
progress_queue.put(("label", _("Operacja anulowana")))
|
||||
return
|
||||
|
||||
# Get authentication session and token
|
||||
@@ -1379,7 +1541,7 @@ class ImageBrowser(tk.Tk):
|
||||
# Check cancellation after login if needed.
|
||||
if cancel_event is not None and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(("label", "Operacja anulowana"))
|
||||
progress_queue.put(("label", _("Operacja anulowana")))
|
||||
return
|
||||
|
||||
# Prepare tags and rating
|
||||
@@ -1410,7 +1572,7 @@ class ImageBrowser(tk.Tk):
|
||||
# Check for cancellation before sending the update request.
|
||||
if cancel_event is not None and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(("label", "Operacja anulowana"))
|
||||
progress_queue.put(("label", _("Operacja anulowana")))
|
||||
return
|
||||
|
||||
# Send update request
|
||||
@@ -1420,23 +1582,23 @@ class ImageBrowser(tk.Tk):
|
||||
if response.status_code == 302:
|
||||
if progress_queue:
|
||||
progress_queue.put(("progress", 100))
|
||||
message = "Tagi zostały zaktualizowane!"
|
||||
message = _("Tagi zostały zaktualizowane!")
|
||||
if not final_tags: # Only show success if not bulk operation
|
||||
messagebox.showinfo("Sukces edycji", message)
|
||||
messagebox.showinfo(_("Sukces edycji"), message)
|
||||
# Update UI state
|
||||
self.after(0, self.update_button_states)
|
||||
return
|
||||
|
||||
# Handle other status codes
|
||||
error_msg = (
|
||||
f"Błąd podczas aktualizacji tagów\nStatus: {response.status_code}"
|
||||
error_msg = _("Błąd podczas aktualizacji tagów\nStatus: {code}").format(
|
||||
code=response.status_code
|
||||
)
|
||||
if response.text:
|
||||
error_msg += f"\nTreść: {response.text}"
|
||||
error_msg += f"\n{_('Treść:')} {response.text}"
|
||||
messagebox.showerror("Błąd edycji", error_msg)
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("Krytyczny błąd edycji", str(e))
|
||||
messagebox.showerror(_("Krytyczny błąd edycji"), str(e))
|
||||
finally:
|
||||
if progress_queue:
|
||||
progress_queue.put(("progress", 100))
|
||||
@@ -1448,12 +1610,18 @@ class ImageBrowser(tk.Tk):
|
||||
i wywołuje upload_file.
|
||||
"""
|
||||
if not messagebox.askyesno(
|
||||
"Potwierdzenie",
|
||||
"Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\nKażdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\nUpewnij się, że tagi są poprawne!",
|
||||
_("Potwierdzenie"),
|
||||
_(
|
||||
"Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\nKażdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\nUpewnij się, że tagi są poprawne!"
|
||||
),
|
||||
):
|
||||
return
|
||||
|
||||
def worker(progress_queue: queue = None, cancel_event: threading.Event = None):
|
||||
def worker(
|
||||
progress_queue: queue.Queue = None,
|
||||
cancel_event: threading.Event = None,
|
||||
secondary_progress_queue: queue.Queue = None,
|
||||
):
|
||||
files_count = len(self.image_files)
|
||||
if progress_queue:
|
||||
progress_queue.put(("mode", "determinate"))
|
||||
@@ -1461,26 +1629,37 @@ class ImageBrowser(tk.Tk):
|
||||
file_idx = 0
|
||||
for file_path in self.image_files:
|
||||
if progress_queue:
|
||||
progress_queue.put(("progress", file_idx * 1.0 / files_count))
|
||||
progress_queue.put(("progress", file_idx * 100 / files_count))
|
||||
progress_queue.put(
|
||||
("label", f"Wysyłam plik {file_idx+1}/{files_count}...")
|
||||
)
|
||||
if cancel_event is not None and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(("label", f"Anulowano operację!"))
|
||||
progress_queue.put(("label", _("Anulowano operację!")))
|
||||
return
|
||||
if not self.uploaded.get(file_path, False):
|
||||
final_tags, final_rating = (
|
||||
self.compute_final_tags_and_rating_for_file(file_path)
|
||||
)
|
||||
print(
|
||||
f"Uploading {file_path} z tagami: {final_tags} i ratingiem: {final_rating}"
|
||||
_(
|
||||
"Wysyłanie {file_path} z tagami: {final_tags} i ratingiem: {final_rating}"
|
||||
).format(
|
||||
file_path=file_path,
|
||||
final_tags=final_tags,
|
||||
final_rating=final_rating,
|
||||
)
|
||||
)
|
||||
self.upload_file(
|
||||
file_path, final_tags=final_tags, final_rating=final_rating
|
||||
file_path,
|
||||
final_tags=final_tags,
|
||||
final_rating=final_rating,
|
||||
progress_queue=secondary_progress_queue,
|
||||
cancel_event=cancel_event,
|
||||
)
|
||||
file_idx += 1
|
||||
if progress_queue:
|
||||
progress_queue.put(("label", f"Przesłano pliki!"))
|
||||
progress_queue.put(("label", _("Przesłano pliki!")))
|
||||
progress_queue.put(("progress", 100))
|
||||
|
||||
self.processing_dialog = ProcessingDialog(self, worker)
|
||||
|
Reference in New Issue
Block a user