Compare commits

...

12 Commits

Author SHA1 Message Date
Hamed
6c26e99dcf new buttons in sys tray 2026-05-12 22:10:22 +03:30
Hamed
a701c0abd3 added restart button 2026-05-06 13:23:50 +03:30
Hamed
8463eb28df added melli internet install 2026-03-30 11:33:37 +03:30
Hamed
c23805320e run on startup silently 2026-01-06 23:04:07 +03:30
Hamed
66c0482ae3 update gitignore 2026-01-06 22:44:59 +03:30
Hamed
9647115ad6 update gitignore 2026-01-06 22:43:48 +03:30
Hamed
7fbd6ca080 gitignore update 2026-01-06 22:42:46 +03:30
Hamed
4163d1cf6e fixed the ensure one instanve only logic. 2025-12-15 20:26:07 +03:30
Hamed
fb12d5a216 correcting the one-instance only mechanism 2025-12-04 17:20:05 +03:30
Hamed
9aa2ff2bd0 gitignore update 2025-12-04 17:07:40 +03:30
Hamed
96a8092716 Remove merge conflict markers from requirements.txt 2025-12-04 17:02:09 +03:30
Hamed
ad7ca318a7 Delete app.lock 2025-12-04 16:59:24 +03:30
9 changed files with 147 additions and 20 deletions

11
.gitignore vendored
View File

@@ -1,6 +1,7 @@
.vscode/settings.json .vscode/settings.json
src/__pycache__/config.cpython-314.pyc __pycache__/
src/__pycache__/startup.cpython-314.pyc *.py[cod]
*$py.class
src/__pycache__/startup.cpython-314.pyc app.lock
src/__pycache__/config.cpython-314.pyc src/__pycache__/
venv

View File

@@ -1 +0,0 @@
9512

19
install-melli.bat Normal file
View File

@@ -0,0 +1,19 @@
@echo off
REM install.bat
REM This script sets up a Python virtual environment and installs required libraries.
REM Make sure to run this script in the project directory.
REM Create Python virtual environment
python -m venv venv
REM Activate the virtual environment
call venv\Scripts\activate.bat
REM Upgrade pip
pip install --upgrade pip -i https://mirror-pypi.runflare.com/simple
REM Install required libraries from requirements.txt
pip install -r requirements.txt --no-cache-dir -i https://mirror-pypi.runflare.com/simple
echo Environment setup complete.
pause

View File

@@ -1,8 +1,5 @@
keyboard==0.13.5 keyboard==0.13.5
pyperclip==1.9.0 pyperclip==1.9.0
pystray==0.19.5 pystray==0.19.5
<<<<<<< HEAD
Pillow==12.0.0 Pillow==12.0.0
======= psutil>=5.8.0
Pillow==12.0.0
>>>>>>> 4143eb7dfbdcc5a646f23c1fc5369779d41c4f4a

4
run_silent.bat Normal file
View File

@@ -0,0 +1,4 @@
@echo off
cd /d "%~dp0"
call venv\Scripts\activate.bat
start "" pythonw src\main.pyw

4
run_silent.vbs Normal file
View File

@@ -0,0 +1,4 @@
Set WshShell = CreateObject("WScript.Shell")
strPath = CreateObject("Scripting.FileSystemObject").GetParentFolderName(WScript.ScriptFullName)
WshShell.Run chr(34) & strPath & "\run_silent.bat" & Chr(34), 0
Set WshShell = Nothing

View File

@@ -11,8 +11,9 @@ import re
from config import load_config, save_config, get_shortcut from config import load_config, save_config, get_shortcut
from startup import add_to_startup, remove_from_startup, is_in_startup from startup import add_to_startup, remove_from_startup, is_in_startup
import os import os
import signal
import atexit import atexit
import sys
import tkinter as tk
def convert_en_characters(input_str): def convert_en_characters(input_str):
""" """
@@ -138,6 +139,68 @@ def quit_app(icon, item):
import os import os
os._exit(0) os._exit(0)
def restart_app(icon, item):
icon.stop()
# Unhook all keyboard hooks before restarting
keyboard.unhook_all()
cleanup_lock()
import subprocess
python = sys.executable
script = os.path.abspath(__file__)
subprocess.Popen([python, script])
os._exit(0)
def change_shortcut_dialog():
"""Show a dialog to capture a new keyboard shortcut."""
root = tk.Tk()
root.title("Change Shortcut")
root.geometry("400x200")
root.resizable(False, False)
# Center the window
root.update_idletasks()
x = (root.winfo_screenwidth() // 2) - (400 // 2)
y = (root.winfo_screenheight() // 2) - (200 // 2)
root.geometry(f"400x200+{x}+{y}")
captured_shortcut = {"value": None}
label = tk.Label(root, text="Press the key combination you want to use\nas a shortcut, then press ESC",
font=("Arial", 12), pady=20)
label.pack()
shortcut_label = tk.Label(root, text="Waiting for input...", font=("Arial", 14, "bold"), fg="blue")
shortcut_label.pack(pady=10)
def capture_shortcut():
try:
# Unregister current hotkey before capturing new one
keyboard.unhook_all_hotkeys()
shortcut = keyboard.read_hotkey(suppress=False)
captured_shortcut["value"] = shortcut
shortcut_label.config(text=f"Captured: {shortcut}")
root.after(1000, root.destroy)
except Exception as e:
shortcut_label.config(text=f"Error: {str(e)}", fg="red")
root.after(2000, root.destroy)
# Start capturing in a separate thread
capture_thread = threading.Thread(target=capture_shortcut, daemon=True)
capture_thread.start()
root.mainloop()
return captured_shortcut["value"]
def on_change_shortcut(icon, item):
"""Handle shortcut change request."""
new_shortcut = change_shortcut_dialog()
if new_shortcut:
config = load_config()
config["shortcut"] = new_shortcut
save_config(config)
# Always restart after shortcut change attempt to ensure hotkeys are properly re-registered
restart_app(icon, item)
def setup_tray(config): def setup_tray(config):
def on_toggle_lang_change(icon, item): def on_toggle_lang_change(icon, item):
config["lang_change"] = not item.checked config["lang_change"] = not item.checked
@@ -153,7 +216,7 @@ def setup_tray(config):
image = Image.open("assets/icon.png") image = Image.open("assets/icon.png")
menu = pystray.Menu( menu = pystray.Menu(
pystray.MenuItem(f'Current Shortcut: {get_shortcut()}',lambda icon, item: None), pystray.MenuItem(f'Shortcut: {get_shortcut()} (click to change)', on_change_shortcut),
pystray.MenuItem( pystray.MenuItem(
'change keyboard language layout after transformation', 'change keyboard language layout after transformation',
on_toggle_lang_change, on_toggle_lang_change,
@@ -164,6 +227,7 @@ def setup_tray(config):
on_toggle_startup, on_toggle_startup,
checked=lambda item: config["run_on_startup"] checked=lambda item: config["run_on_startup"]
), ),
pystray.MenuItem('Restart', restart_app),
pystray.MenuItem('Quit', quit_app) pystray.MenuItem('Quit', quit_app)
) )
icon = pystray.Icon("oopswronglang", image, "OopsWrongLang", menu) icon = pystray.Icon("oopswronglang", image, "OopsWrongLang", menu)
@@ -178,22 +242,52 @@ def cleanup_lock():
except OSError: except OSError:
pass pass
def is_process_running(pid):
"""Check if a process with the given PID is still running."""
try:
import psutil
return psutil.pid_exists(pid)
except:
# Fallback if psutil is not available
try:
os.kill(pid, 0) # Signal 0 doesn't kill, just checks if process exists
return True
except OSError:
return False
def ensure_single_instance(): def ensure_single_instance():
"""Ensure only one instance of the app is running. Exit if another instance exists."""
if os.path.exists(LOCK_FILE): if os.path.exists(LOCK_FILE):
try: try:
# Read the PID from the lock file
with open(LOCK_FILE, 'r') as f: with open(LOCK_FILE, 'r') as f:
pid = int(f.read().strip()) old_pid = int(f.read().strip())
if pid != os.getpid(): # Check if the process with that PID is still running
if is_process_running(old_pid):
# Another instance is running, show message and exit
import ctypes
ctypes.windll.user32.MessageBoxW(
0,
"Another instance of OopsWrongLang is already running.\n\nPlease close the existing instance first (check your system tray).",
"OopsWrongLang - Already Running",
0x40 # MB_ICONINFORMATION
)
os._exit(1)
else:
# Process is not running, remove the stale lock file
try: try:
os.kill(pid, signal.SIGTERM) os.remove(LOCK_FILE)
print(f"Killed existing instance with PID {pid}")
time.sleep(0.5)
except OSError: except OSError:
pass pass
except (ValueError, OSError): except (ValueError, OSError):
pass # Lock file exists but is invalid or unreadable, clean it up and continue
try:
os.remove(LOCK_FILE)
except OSError:
pass
# Create lock file with our PID
with open(LOCK_FILE, 'w') as f: with open(LOCK_FILE, 'w') as f:
f.write(str(os.getpid())) f.write(str(os.getpid()))
@@ -203,7 +297,12 @@ def main():
ensure_single_instance() ensure_single_instance()
config = load_config() config = load_config()
# Sync startup state just in case it was changed externally # Sync startup state just in case it was changed externally
config["run_on_startup"] = is_in_startup() if is_in_startup():
config["run_on_startup"] = True
# Update the registry key to point to the current location (in case the app was moved)
add_to_startup()
else:
config["run_on_startup"] = False
save_config(config) save_config(config)
# Start the tray icon in a separate thread # Start the tray icon in a separate thread

View File

@@ -2,7 +2,11 @@ import os
import winreg import winreg
APP_NAME = "OopsWrongLang" APP_NAME = "OopsWrongLang"
RUN_BAT_PATH = os.path.abspath("run.bat") # Get the directory of the current script (src)
current_dir = os.path.dirname(os.path.abspath(__file__))
# The project root is one level up
project_root = os.path.dirname(current_dir)
RUN_BAT_PATH = os.path.join(project_root, "run_silent.vbs")
def add_to_startup(): def add_to_startup():
""" """