Skip to content

Commit fad1e7a

Browse files
authored
Merge pull request #10 from Ati1707/main
Added menu and removed buttons
2 parents 164a13c + 2a28bc6 commit fad1e7a

File tree

7 files changed

+162
-87
lines changed

7 files changed

+162
-87
lines changed

.github/workflows/test.yml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches:
6+
- "main"
7+
8+
env:
9+
release_name: 0.1.${{github.run_number}}
10+
11+
jobs:
12+
build:
13+
runs-on: windows-latest
14+
15+
steps:
16+
- name: Checkout code
17+
uses: actions/checkout@v4
18+
19+
- name: Set up Python
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: '3.10'
23+
cache: 'pip'
24+
25+
- name: Install dependencies
26+
run: |
27+
pip install --upgrade pip
28+
pip install pyinstaller -r requirements.txt
29+
30+
- name: Build with PyInstaller
31+
run: >
32+
pyinstaller --onefile main.py
33+
--name TextAutoTranslate
34+
--distpath dist/TextAutoTranslate
35+
--workpath .pyinstaller-cache
36+
--specpath .pyinstaller-cache
37+
--hidden-import=urllib3.contrib.resolver.system
38+
--hidden-import=urllib3.contrib.hface.protocols.http1
39+
--hidden-import=urllib3.contrib.hface.protocols.http2
40+
--hidden-import=urllib3.contrib.hface.protocols.http3
41+
--noconsole

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# TextAutoTranslate 🌍📝
22

33
[![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
4-
![Python Version](https://img.shields.io/badge/Python-3.8%2B-blue)
4+
![Python Version](https://img.shields.io/badge/python-3.10-%233572A5?logo=python&logoColor=white)
55

66
A GUI tool to instantly translate selected text from files. Perfect for multilingual content editing!
77

8-
![Demo Screenshot](https://github.com/user-attachments/assets/748d6b8e-27dc-4421-b3c8-8e4848323bed)
8+
![Demo Screenshot](https://github.com/user-attachments/assets/4c3841af-bb24-436b-80ba-f7688dc8937d)
99

1010
## Features ✨
1111
- **Real-time translation** of selected text in files
@@ -32,8 +32,8 @@ Launch the application:
3232
python main.py
3333
```
3434

35-
- Open a text file (**File > Open** or `Ctrl+O`) or copy your text in the upper panel
36-
- Select text in the editor (translations start after 1 second of selection)
35+
- Open a text file or copy your text in the upper panel
36+
- Select text in the editor (translation start after 1 second of selection)
3737
- View translation in the bottom panel
3838

3939
## Dependencies 📦

app.py

+100-58
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
import threading
22
from PySide6.QtWidgets import (
3-
QWidget, QGridLayout, QTextEdit, QPushButton,
4-
QComboBox, QFileDialog, QMessageBox, QHBoxLayout
3+
QMainWindow,
4+
QWidget,
5+
QGridLayout,
6+
QTextEdit,
7+
QComboBox,
8+
QFileDialog,
9+
QMessageBox,
10+
QHBoxLayout,
511
)
12+
from PySide6.QtGui import QAction
613
from PySide6.QtCore import QTimer, Qt
714
from charset_normalizer import from_bytes
815
from translation_worker import TranslationWorker
916

10-
TRANSLATION_PROVIDERS = {
11-
"Google", "KoboldCPP"
12-
}
17+
TRANSLATION_PROVIDERS = {"Google", "KoboldCPP"}
1318

1419

15-
class TranslatorApp(QWidget):
16-
20+
class TranslatorApp(QMainWindow):
1721
def __init__(self, llm_url):
1822
super().__init__()
1923
self.llm_url = llm_url
2024
self.thread = None
2125
self.current_file_path = None
2226
self.language_combo = None
23-
self.open_button = None
24-
self.save_button = None
2527
self.translation_output = None
2628
self.text_edit = None
2729
self.worker = None
@@ -33,72 +35,113 @@ def __init__(self, llm_url):
3335
self.selection_timer.setSingleShot(True)
3436
self.selection_timer.timeout.connect(self.process_selection)
3537
self.translator_combo = None
36-
self.init_ui()
3738

39+
# Create central widget and main layout
40+
central_widget = QWidget()
41+
self.setCentralWidget(central_widget)
42+
self.main_layout = QGridLayout(central_widget)
43+
self.main_layout.setContentsMargins(10, 10, 10, 10)
3844

45+
self.init_ui()
46+
self.create_menus()
47+
48+
def create_menus(self):
49+
menu_bar = self.menuBar()
50+
51+
# File menu
52+
file_menu = menu_bar.addMenu("&File")
53+
open_action = QAction("&Open", self)
54+
open_action.triggered.connect(self.open_file)
55+
open_action.setShortcut("Ctrl+O")
56+
file_menu.addAction(open_action)
57+
58+
save_action = QAction("&Save", self)
59+
save_action.triggered.connect(self.save_file)
60+
save_action.setShortcut("Ctrl+S")
61+
file_menu.addAction(save_action)
62+
63+
file_menu.addSeparator()
64+
exit_action = QAction("&Exit", self)
65+
exit_action.triggered.connect(self.close)
66+
file_menu.addAction(exit_action)
67+
68+
# Edit menu
69+
edit_menu = menu_bar.addMenu("&Edit")
70+
undo_action = QAction("&Undo", self)
71+
undo_action.triggered.connect(self.text_edit.undo)
72+
undo_action.setShortcut("Ctrl+Z")
73+
edit_menu.addAction(undo_action)
74+
75+
redo_action = QAction("&Redo", self)
76+
redo_action.triggered.connect(self.text_edit.redo)
77+
redo_action.setShortcut("Ctrl+Y")
78+
edit_menu.addAction(redo_action)
79+
80+
# Help menu
81+
help_menu = menu_bar.addMenu("&Help")
82+
about_action = QAction("&About", self)
83+
about_action.triggered.connect(self.show_about)
84+
help_menu.addAction(about_action)
85+
86+
def show_about(self):
87+
about_box = QMessageBox(self)
88+
about_box.setWindowTitle("About TextAutoTranslate")
89+
about_box.setTextFormat(Qt.TextFormat.RichText)
90+
about_box.setText(
91+
"A translation tool with multiple providers<br><br>"
92+
"Version 0.1<br>"
93+
"Copyright © 2025 Ati1707<br><br>"
94+
"<a href='https://github.com/Ati1707/TextAutoTranslate'>GitHub Repository</a>"
95+
)
96+
about_box.exec()
3997

4098
def init_ui(self):
41-
layout = QGridLayout(self)
42-
layout.setContentsMargins(10, 10, 10, 10)
43-
4499
# Text Editor
45100
self.text_edit = QTextEdit()
46101
self.text_edit.setAcceptRichText(False)
47102
self.text_edit.setFontFamily("Courier New")
48103
self.text_edit.setFontPointSize(12)
49-
layout.addWidget(self.text_edit, 0, 0, 1, 2)
104+
self.main_layout.addWidget(self.text_edit, 0, 0, 1, 2)
50105

51106
# Translation Output
52107
self.translation_output = QTextEdit()
53108
self.translation_output.setFontFamily("Courier New")
54109
self.translation_output.setFontPointSize(12)
55110
self.translation_output.setReadOnly(True)
56-
layout.addWidget(self.translation_output, 1, 0, 1, 2)
57-
58-
# Buttons Layout
59-
buttons_layout = QHBoxLayout()
60-
61-
# Open File Button
62-
self.open_button = QPushButton("Open File")
63-
self.open_button.clicked.connect(self.open_file)
64-
buttons_layout.addWidget(self.open_button)
65-
66-
# Save File Button
67-
self.save_button = QPushButton("Save File")
68-
self.save_button.clicked.connect(self.save_file)
69-
buttons_layout.addWidget(self.save_button)
111+
self.main_layout.addWidget(self.translation_output, 1, 0, 1, 2)
70112

71113
# Translator Selection Combo Box
72114
self.translator_combo = QComboBox()
73115
self.translator_combo.addItems(list(TRANSLATION_PROVIDERS))
74116
self.translator_combo.setCurrentIndex(0)
75117

76-
# Combine buttons and translator combo in a horizontal layout
118+
# Header layout (left side)
77119
left_header_layout = QHBoxLayout()
78-
left_header_layout.addLayout(buttons_layout)
79120
left_header_layout.addWidget(self.translator_combo)
80-
left_header_layout.addStretch() # Push elements to the left
121+
left_header_layout.addStretch()
81122

82123
# Add combined layout to grid
83-
layout.addLayout(left_header_layout, 2, 0)
124+
self.main_layout.addLayout(left_header_layout, 2, 0)
84125

85126
# Language Combo Box
86127
self.language_combo = QComboBox()
87128
self.language_combo.addItems(self.languages)
88129
self.language_combo.setCurrentIndex(-1)
89130
self.language_combo.setPlaceholderText("Select Target Language")
90-
layout.addWidget(self.language_combo, 2, 1, alignment=Qt.AlignmentFlag.AlignRight)
131+
self.main_layout.addWidget(
132+
self.language_combo, 2, 1, alignment=Qt.AlignmentFlag.AlignRight
133+
)
91134

92-
# Update language combo label when translator changes
93-
self.translator_combo.currentTextChanged.connect(self.update_language_combo_label)
135+
# Connect signals
136+
self.translator_combo.currentTextChanged.connect(
137+
self.update_language_combo_label
138+
)
139+
self.text_edit.selectionChanged.connect(self.handle_selection)
94140

95141
# Set row stretch factors
96-
layout.setRowStretch(0, 7)
97-
layout.setRowStretch(1, 2)
98-
layout.setRowStretch(2, 1)
99-
100-
# Connect selection change handler
101-
self.text_edit.selectionChanged.connect(self.handle_selection)
142+
self.main_layout.setRowStretch(0, 7)
143+
self.main_layout.setRowStretch(1, 2)
144+
self.main_layout.setRowStretch(2, 1)
102145

103146
def update_window_title(self):
104147
"""Update window title with current file path if available"""
@@ -115,10 +158,7 @@ def update_language_combo_label(self, translator):
115158

116159
def open_file(self):
117160
file_path, _ = QFileDialog.getOpenFileName(
118-
self,
119-
"Open File",
120-
"",
121-
"All Files (*)"
161+
self, "Open File", "", "All Files (*)"
122162
)
123163

124164
if file_path:
@@ -127,14 +167,14 @@ def open_file(self):
127167
with open(file_path, "rb") as f:
128168
raw_data = f.read()
129169
result = from_bytes(raw_data).best()
130-
encoding = result.encoding if result else 'utf-8'
170+
encoding = result.encoding if result else "utf-8"
131171

132172
try:
133173
with open(file_path, "r", encoding=encoding) as file:
134174
content = file.read()
135175
except (UnicodeDecodeError, LookupError):
136176
try:
137-
with open(file_path, "r", encoding='utf-16') as file:
177+
with open(file_path, "r", encoding="utf-16") as file:
138178
content = file.read()
139179
except Exception as e:
140180
content = f"Error: Failed to decode file - {str(e)}"
@@ -147,11 +187,14 @@ def save_file(self):
147187
# Overwrite existing file
148188
content = self.text_edit.toPlainText()
149189
try:
150-
reply = QMessageBox.question(self, "Confirm File Overwrite",
151-
f"You are about to overwrite:{self.current_file_path}"
152-
f"This will permanently replace the existing file. Are you sure you want to continue?",
153-
QMessageBox.StandardButton.Yes,
154-
QMessageBox.StandardButton.No)
190+
reply = QMessageBox.question(
191+
self,
192+
"Confirm File Overwrite",
193+
f"You are about to overwrite:{self.current_file_path}"
194+
f"This will permanently replace the existing file. Are you sure you want to continue?",
195+
QMessageBox.StandardButton.Yes,
196+
QMessageBox.StandardButton.No,
197+
)
155198
if reply == QMessageBox.StandardButton.Yes:
156199
with open(self.current_file_path, "w", encoding="utf-8") as file:
157200
file.write(content)
@@ -160,10 +203,7 @@ def save_file(self):
160203
else:
161204
# Save as new file
162205
file_path, _ = QFileDialog.getSaveFileName(
163-
self,
164-
"Save File",
165-
"",
166-
"All Files (*)"
206+
self, "Save File", "", "All Files (*)"
167207
)
168208
if file_path:
169209
content = self.text_edit.toPlainText()
@@ -173,7 +213,9 @@ def save_file(self):
173213
self.current_file_path = file_path
174214
self.update_window_title()
175215
except Exception as e:
176-
QMessageBox.critical(self, "Error", f"Failed to save file: {str(e)}")
216+
QMessageBox.critical(
217+
self, "Error", f"Failed to save file: {str(e)}"
218+
)
177219

178220
def handle_selection(self):
179221
if self.selection_timer.isActive():
@@ -199,4 +241,4 @@ def start_translation_thread(self, text):
199241
self.worker = TranslationWorker(text, translator, language, self.llm_url)
200242
self.thread = threading.Thread(target=self.worker.run)
201243
self.worker.finished.connect(self.update_translation_output)
202-
self.thread.start()
244+
self.thread.start()

main.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
from app import TranslatorApp
55

66
if __name__ == "__main__":
7-
parser = argparse.ArgumentParser(description='TextAutoTranslate Application')
8-
parser.add_argument('--llm-url',
9-
default='http://localhost:5001/api/v1/generate',
10-
help='URL for the LLM translation API')
7+
parser = argparse.ArgumentParser(description="TextAutoTranslate Application")
8+
parser.add_argument(
9+
"--llm-url",
10+
default="http://localhost:5001/api/v1/generate",
11+
help="URL for the LLM translation API",
12+
)
1113
args = parser.parse_args()
1214

1315
app = QApplication(sys.argv)
1416
window = TranslatorApp(llm_url=args.llm_url)
1517
window.show()
16-
sys.exit(app.exec())
18+
sys.exit(app.exec())

providers/llm/koboldcpp.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,13 @@ def translate(self, text, source_lang=None, target_lang="English"):
5353
"top_a": 0,
5454
"top_k": 100,
5555
"top_p": 1,
56-
"typical": 1
56+
"typical": 1,
5757
}
5858

5959
response = requests.post(
60-
self.api_url,
61-
json=data,
62-
headers={"Content-Type": "application/json"}
60+
self.api_url, json=data, headers={"Content-Type": "application/json"}
6361
)
6462

6563
if response.status_code == 200:
6664
return response.json()["results"][0]["text"]
67-
raise Exception(f"API request failed ({response.status_code})")
65+
raise Exception(f"API request failed ({response.status_code})")

providers/translation/google.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
from translators import translate_text
22

3+
34
class GoogleTranslator:
45
@staticmethod
5-
def translate(text, target_lang='en', **kwargs):
6-
return translate_text(
7-
text,
8-
translator='google',
9-
to_language=target_lang
10-
)
6+
def translate(text, target_lang="en", **kwargs):
7+
return translate_text(text, translator="google", to_language=target_lang)

0 commit comments

Comments
 (0)