Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 141 additions & 13 deletions LivescriptV2.01.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@
DEVELOPER_URL = "https://github.com/sally4d"
LinkedIn_URL = "https://www.linkedin.com/in/oscurprof/"
SUPPORT_URL = "https://oscurprofundo.gumroad.com/l/dmkbes"
BASE_SCREEN_DPI = 96
BASE_TK_SCALING = BASE_SCREEN_DPI / 72
MIN_UI_SCALING = 0.75
MAX_UI_SCALING = 3.0
UI_SCALING_CHECK_INTERVAL_MS = 3000
MAX_DIALOG_SCREEN_FRACTION = 0.9

# --- Language Model Definitions ---
LANGUAGE_MODELS = {
Expand Down Expand Up @@ -306,13 +312,139 @@ def save_settings(self, settings):
print(f"Error saving settings: {e}")


def clamp_ui_scaling(value):
return max(MIN_UI_SCALING, min(MAX_UI_SCALING, value))


def parse_positive_float(value):
try:
parsed_value = float(value)
except (TypeError, ValueError):
return None
return parsed_value if parsed_value > 0 else None


def get_environment_ui_scaling():
for variable_name in ("QT_SCALE_FACTOR", "GDK_SCALE", "ELM_SCALE", "CLUTTER_SCALE"):
scaling = parse_positive_float(os.environ.get(variable_name))
if scaling and scaling != 1:
return scaling, variable_name

scaling = parse_positive_float(os.environ.get("GDK_DPI_SCALE"))
if scaling and scaling != 1 and "GDK_SCALE" not in os.environ:
return scaling, "GDK_DPI_SCALE"

return None, None


def get_xrdb_ui_scaling():
try:
result = subprocess.run(["xrdb", "-query"], capture_output=True, text=True, timeout=0.5, check=False)
except (FileNotFoundError, subprocess.SubprocessError, OSError):
return None, None

for line in result.stdout.splitlines():
if line.lower().startswith("xft.dpi"):
_, _, raw_dpi = line.partition(":")
dpi = parse_positive_float(raw_dpi.strip())
if dpi:
return dpi / BASE_SCREEN_DPI, "Xft.dpi"

return None, None


def get_tk_ui_scaling(root):
try:
root.update_idletasks()
dpi = root.winfo_fpixels("1i")
except tk.TclError:
return None, None

return dpi / BASE_SCREEN_DPI, "Tk DPI"


def detect_system_ui_scaling(root):
fallback_scaling = 1.0
fallback_source = "default"

for detector in (get_environment_ui_scaling, get_xrdb_ui_scaling, lambda: get_tk_ui_scaling(root)):
scaling, source = detector()
if scaling:
scaling = clamp_ui_scaling(scaling)
if abs(scaling - 1.0) >= 0.05:
return scaling, source
fallback_scaling = scaling
fallback_source = source

return fallback_scaling, fallback_source


def apply_system_ui_scaling(root, log=True):
scaling, source = detect_system_ui_scaling(root)

try:
root.tk.call("tk", "scaling", BASE_TK_SCALING * scaling)
except tk.TclError:
pass

ctk.set_widget_scaling(scaling)
ctk.set_window_scaling(scaling)
if log:
print(f"Using UI scaling: {scaling:.2f}x ({source})")
return scaling


def get_scaled_dialog_geometry(window, width, height):
scaling, _ = detect_system_ui_scaling(window)
screen_width = window.winfo_screenwidth()
screen_height = window.winfo_screenheight()
max_width = screen_width * MAX_DIALOG_SCREEN_FRACTION
max_height = screen_height * MAX_DIALOG_SCREEN_FRACTION

scaled_width = min(width * scaling, max_width)
scaled_height = min(height * scaling, max_height)
geometry_width = max(1, round(scaled_width / scaling))
geometry_height = max(1, round(scaled_height / scaling))
x = max(0, round((screen_width - scaled_width) / 2))
y = max(0, round((screen_height - scaled_height) / 2))

return f"{geometry_width}x{geometry_height}+{x}+{y}"


def monitor_system_ui_scaling(root, caption_window, last_scaling):
try:
if not root.winfo_exists():
return

scaling, source = detect_system_ui_scaling(root)
if abs(scaling - last_scaling) >= 0.05:
try:
root.tk.call("tk", "scaling", BASE_TK_SCALING * scaling)
except tk.TclError:
pass

ctk.set_widget_scaling(scaling)
ctk.set_window_scaling(scaling)
caption_window.apply_settings(caption_window.settings)
for child in root.winfo_children():
if isinstance(child, (AboutWindow, SettingsWindow)):
child.center_window()
print(f"Updated UI scaling: {scaling:.2f}x ({source})")
last_scaling = scaling

root.after(UI_SCALING_CHECK_INTERVAL_MS, monitor_system_ui_scaling, root, caption_window, last_scaling)
except tk.TclError:
pass


class AboutWindow(ctk.CTkToplevel):
"""The 'About' window providing app information and links."""
WINDOW_WIDTH = 550
WINDOW_HEIGHT = 450

def __init__(self, parent):
super().__init__(parent)
self.title("About Live Captions")
self.geometry("550x450")
self.transient(parent)
self.center_window()
self.grid_columnconfigure(0, weight=1)
Expand Down Expand Up @@ -367,20 +499,17 @@ def __init__(self, parent):

def center_window(self):
self.update_idletasks()
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
x = (screen_width - 550) // 2
y = (screen_height - 450) // 2
self.geometry(f"550x450+{x}+{y}")
self.geometry(get_scaled_dialog_geometry(self, self.WINDOW_WIDTH, self.WINDOW_HEIGHT))


class SettingsWindow(ctk.CTkToplevel):
"""The settings window GUI, built with customtkinter."""
WINDOW_WIDTH = 540
WINDOW_HEIGHT = 850

def __init__(self, parent, settings_manager, caption_window, restart_callback):
super().__init__(parent)
self.title("Settings")
self.geometry("540x850")
self.transient(parent)
self.center_window()

Expand All @@ -402,11 +531,7 @@ def __init__(self, parent, settings_manager, caption_window, restart_callback):

def center_window(self):
self.update_idletasks()
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
x = (screen_width - 540) // 2
y = (screen_height - 850) // 2
self.geometry(f"540x850+{x}+{y}")
self.geometry(get_scaled_dialog_geometry(self, self.WINDOW_WIDTH, self.WINDOW_HEIGHT))

def setup_ui(self):
self.grid_rowconfigure(0, weight=1)
Expand Down Expand Up @@ -1050,6 +1175,7 @@ def main():

temp_root = ctk.CTk()
temp_root.withdraw()
apply_system_ui_scaling(temp_root, log=False)

if not os.path.isdir(settings['model_path']):
messagebox.showerror("Model Not Found",
Expand All @@ -1058,6 +1184,7 @@ def main():
temp_root.destroy()

root = tk.Tk()
current_ui_scaling = apply_system_ui_scaling(root)

def restart_application():
print("Restarting application...")
Expand Down Expand Up @@ -1090,6 +1217,7 @@ def restart_application():
recognizer_thread.start()

app = CaptionWindow(root, settings_manager, restart_application)
root.after(UI_SCALING_CHECK_INTERVAL_MS, monitor_system_ui_scaling, root, app, current_ui_scaling)

if is_first_run or settings.get('show_about_on_startup', True):
root.after(100, app.open_about_window)
Expand All @@ -1107,4 +1235,4 @@ def restart_application():


if __name__ == "__main__":
main()
main()