from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QListWidget, QTextEdit, QHBoxLayout, QPushButton, QVBoxLayout, QLineEdit, QLabel from PyQt5.QtGui import QIcon, QFont, QColor from PyQt5 import QtCore, QtWidgets from selenium.webdriver.firefox.service import Service from selenium.webdriver.firefox.options import Options from selenium import webdriver from pydub import AudioSegment from bs4 import BeautifulSoup from scipy.io import wavfile import urllib.parse as up import sounddevice as sd import http.client import traceback import tempfile import textwrap import requests import creds import json import sys import re import io class CopyableListWidget(QListWidget): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_C and event.modifiers() == QtCore.Qt.ControlModifier: self.copy_selected_text() else: super().keyPressEvent(event) def contextMenuEvent(self, event): menu = QtWidgets.QMenu(self) copy_action = menu.addAction("Copy") copy_action.triggered.connect(self.copy_selected_text) menu.exec_(event.globalPos()) def copy_selected_text(self): selected_text = self.currentItem().text() if self.currentItem() else "" selected_text = selected_text.split("(", 1)[0].strip() # remove text within parentheses QtWidgets.QApplication.clipboard().setText(selected_text) class MyWindow(QMainWindow): def __init__(self): super().__init__() # Set window title self.setWindowTitle("Help") # Set window icon icon_path = r"C:\Users\jurad\Desktop\Capture2Text_v4.6.3_64bit\Bot Assets\GUI_widget.png" self.setWindowIcon(QIcon(icon_path)) # Create central widget central_widget = QWidget(self) self.setCentralWidget(central_widget) # Create layout layout = QVBoxLayout() # Create QLineEdit and Search Button self.search_edit = QLineEdit(self) search_button = QPushButton("Search") search_button.clicked.connect(self.translate) # Connect returnPressed signal of QLineEdit to clicked signal of search button self.search_edit.returnPressed.connect(search_button.click) # Create widget for the search box and button search_widget = QWidget(self) search_layout = QHBoxLayout() search_layout.addWidget(QLabel("Enter text here:")) search_layout.addWidget(self.search_edit) search_layout.addWidget(search_button) search_widget.setLayout(search_layout) # Add search widget to layout layout.addWidget(search_widget) # Create non-clickable textbox self.non_clickable_textbox = QLabel(self) self.non_clickable_textbox.setText("Top:") self.non_clickable_textbox.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight) search_layout.addWidget(self.non_clickable_textbox) # Create icon layout icon_layout = QHBoxLayout() # Create icons icon1 = QPushButton() icon1.setIcon(QIcon("C:/Users/jurad/Desktop/Capture2Text_v4.6.3_64bit/Bot Assets/jisho_icon.png")) icon1.setFixedSize(75, 75) icon1.setIconSize(QtCore.QSize(75, 75)) icon1.setToolTip("Show Details") icon1.clicked.connect(self.show_details) icon2 = QPushButton() icon2.setIcon(QIcon(r"C:\Users\jurad\Desktop\Capture2Text_v4.6.3_64bit\Bot Assets\reibun_icon.png")) icon2.setFixedSize(75, 75) icon2.setIconSize(QtCore.QSize(75, 75)) icon2.setToolTip("Show Sentences") icon2.clicked.connect(self.sentences) icon3 = QPushButton() icon3.setIcon(QIcon(r"C:\Users\jurad\Desktop\Capture2Text_v4.6.3_64bit\Bot Assets\play_audio_icon.png")) icon3.setFixedSize(75, 75) icon3.setIconSize(QtCore.QSize(75, 75)) icon3.setToolTip("Play Audio") icon3.clicked.connect(self.play_example_audio) icon4 = QPushButton() icon4.setIcon(QIcon(r"C:\Users\jurad\Desktop\Capture2Text_v4.6.3_64bit\Bot Assets\wanikani_icon.png")) icon4.setFixedSize(75, 75) icon4.setIconSize(QtCore.QSize(75, 75)) icon4.setToolTip("Play Audio") icon4.clicked.connect(self.wanikani) icon5 = QPushButton(QIcon("icon5.png"), "") icon5.setFixedSize(75, 75) icon5.setToolTip("Function 5") icon5.clicked.connect(self.function5) # Add icons to icon layout icon_layout.addWidget(icon1) icon_layout.addWidget(icon2) icon_layout.addWidget(icon3) icon_layout.addWidget(icon4) icon_layout.addWidget(icon5) # Create listbox and QTextEdit self.listbox = CopyableListWidget(self) self.listbox.currentItemChanged.connect(self.update_commonality) self.text_edit = QTextEdit(self) # Set the font size to 14 font = QFont() font.setPointSize(14) self.listbox.setFont(font) self.text_edit.setFont(font) # Create widget for the listbox and QTextEdit list_text_widget = QWidget(self) list_text_layout = QHBoxLayout() list_text_layout.addWidget(self.listbox) list_text_layout.addWidget(self.text_edit) list_text_widget.setLayout(list_text_layout) # Add widgets to layout layout.addLayout(icon_layout) layout.addWidget(list_text_widget) # Set layout to central widget central_widget.setLayout(layout) def translate(self): try: translate = self.search_edit.text() jisho_url = f"https://jisho.org/api/v1/search/words?keyword={up.quote(translate)}" data = requests.get(jisho_url).json()["data"] self.d = {} for i in data: word = i['japanese'][0].get('word', '') reading = i['japanese'][0].get('reading', '') if not word: word = reading key = word + ' (' + reading + ')' self.d[key] = i self.listbox.clear() for i, r in enumerate(self.d): self.listbox.addItem(r) self.listbox.setCurrentRow(0) self.highlight_common_words() except Exception as e: print(f"An error occurred: {e}") def show_details(self): self.text_edit.clear() item = self.listbox.currentItem() if item is not None: result = self.d[item.text()] self.text_edit.clear() self.text_edit.append(f"Japanese: {result['japanese'][0].get('word', '')}\n") if "reading" in result["japanese"][0]: self.text_edit.append(f"Reading: {result['japanese'][0]['reading']}\n") self.text_edit.append("English: ") for m in result["senses"]: if "english_definitions" in m: self.text_edit.append(f"{', '.join(m['english_definitions'])}; ") if "tags" in m and "Common word" in m["tags"]: self.text_edit.append("(Common word); ") self.scroll_to_top() def sentences(self): self.text_edit.clear() selected_word = self.listbox.currentItem().text().split("(", 1)[0].strip() if selected_word: # Set up Selenium WebDriver binary_path = 'C:\\Users\\jurad\\AppData\\Local\\Mozilla Firefox\\firefox.exe' options = Options() options.binary_location = binary_path options.add_argument('-headless') # Run WebDriver invisibly service = Service(executable_path='c:\\webdrivers\\geckodriver.exe') driver = webdriver.Firefox(service=service, options=options) # navigate to website driver.get(f"https://www.kanshudo.com/searcht?q={up.quote(selected_word)}") # get HTML html = driver.page_source # create soup soup = BeautifulSoup(html, 'html.parser') # find the desired sentences tatoeba_divs = soup.find_all("div", class_="tatoeba") tatoeba_divs = list(filter(lambda x: x.find("div", class_="furigana") is not None, tatoeba_divs)) self.text_edit.clear() for tatoeba_div in tatoeba_divs: # Get furigana text furigana_list = [furigana.get_text() for furigana in tatoeba_div.find_all("div", class_="furigana")] # Extract the Japanese sentence sentence = tatoeba_div.get_text(strip=True).replace("(click the icon for English translation)", "") sentence = sentence.replace("。", "。\n") # Remove furigana from the sentence full_sentence = sentence for furi in furigana_list: full_sentence = full_sentence.replace(furi, "", 1) # Print the Japanese sentence without furigana self.text_edit.append(full_sentence) driver.quit() self.scroll_to_top() def update_commonality(self): s = self.listbox.currentRow() if s == -1: return None i = s result = self.d[self.listbox.item(i).text()] word = result['japanese'][0].get('word', '') if not word: return None try: url = f'https://jpdb.io/search?q={up.quote(word)}' response = requests.get(url) soup = BeautifulSoup(response.content, 'html.parser') commonality_tag = soup.find('div', class_='tag tooltip') if commonality_tag: commonality_text = commonality_tag.text.strip() commonality_number = re.search(r'\d+', commonality_text) if commonality_number: commonality = commonality_number.group() else: commonality = "N/A" else: commonality = "N/A" except Exception as e: print(f"An error occurred in update_commonality: {e}") commonality = "N/A" self.non_clickable_textbox.setText(f"Top\n {commonality}") def play_audio(self, audio_url): if not audio_url: print("Invalid audio URL") return response = requests.get(audio_url) if response.status_code == 200: audio_data = io.BytesIO(response.content) audio_segment = AudioSegment.from_file(audio_data, format="mp3") self.play_with_sounddevice(audio_segment) else: print("Error fetching audio:", response.status_code) def play_example_audio(self): try: s = self.listbox.currentRow() if s == -1: return i = s result = self.d[self.listbox.item(i).text()] japanese_text = result['japanese'][0].get('word', '') if not japanese_text: print("No Japanese text found.") return # Post request to TTSMP3 website url = "https://ttsmp3.com/makemp3_new.php" data = { "msg": f'{japanese_text}', "lang": "Mizuki", "source": "ttsmp3" } headers = { "Content-Type": "application/x-www-form-urlencoded" } response = requests.post(url, data=data, headers=headers) if response.status_code == 200: try: response_json = json.loads(response.text) if 'URL' in response_json: audio_url = response_json['URL'] self.play_audio(audio_url) else: print("No audio URL found in response.") except json.JSONDecodeError: print("Error parsing response JSON.") else: print("Error sending POST request:", response.status_code) except Exception as e: print("Error occurred:", str(e)) traceback.print_exc() def play_with_sounddevice(self, seg): with tempfile.NamedTemporaryFile('w+b', suffix='.wav', delete=False) as f: seg.export(f.name, 'wav') fs, data = wavfile.read(f.name) sd.play(data, fs) sd.wait() def wanikani(self): self.text_edit.clear() s = self.listbox.currentRow() if s == -1: return i = s result = self.d[self.listbox.item(i).text()] word = result['japanese'][0].get('word', '') if not word: self.text_edit.append("No Japanese word found in the selection.") return wanikani_url = f'https://api.wanikani.com/v2/subjects?types=vocabulary&filter[character]={up.quote(word)}' print(f"WaniKani URL: {wanikani_url}") self.text_edit.append(f"Selected word: {word}") next_url = f"/v2/subjects?types=vocabulary&filter[character]={up.quote(word)}" matching_data = None while next_url: conn = http.client.HTTPSConnection("api.wanikani.com") conn.request("GET", next_url, headers={ 'Authorization': f'Bearer {creds.wanikani_creds}', 'Content-Type': 'application/json' }) response = conn.getresponse() response_text = response.read().decode("utf-8") if response.status == 200: data = json.loads(response_text) for item in data['data']: if item['data']['slug'] == word: matching_data = item['data'] break if not matching_data: next_url = data.get('pages', {}).get('next_url') else: next_url = None else: self.text_edit.append(f"Error fetching information: {response.status}") return if not matching_data: self.text_edit.append(f'The word {word} does not exist on Wanikani.') return vocabulary = matching_data meanings = [meaning['meaning'] for meaning in vocabulary['meanings']] readings = [reading['reading'] for reading in vocabulary['readings'] if reading['primary']] word_type = ', '.join(vocabulary['parts_of_speech']) self.text_edit.append(f"\nWord Type: {word_type}") self.text_edit.append(f"\nPrimary Meaning(s): {meanings[0]}") if len(meanings) > 1: self.text_edit.append(f"Alternative: {' / '.join(meanings[1:])}") self.text_edit.append(f"\nPrimary Reading(s): {readings[0]}") if len(readings) > 1: self.text_edit.append(f"Alternative: {' / '.join(readings[1:])}") meaning_mnemonic = vocabulary.get('meaning_mnemonic', '') meaning_mnemonic = re.sub(r'|||||', '', meaning_mnemonic) reading_mnemonic = vocabulary.get('reading_mnemonic', '') reading_mnemonic = re.sub(r'|||||', '', reading_mnemonic) self.text_edit.append(f"Meaning Mnemonic: {textwrap.fill(meaning_mnemonic, 80)}") self.text_edit.append(f"\nReading Mnemonic: {textwrap.fill(reading_mnemonic, 80)}") context_sentences = vocabulary.get('context_sentences', []) if context_sentences: self.text_edit.append("\nContext Sentences:") for sentence in context_sentences: self.text_edit.append(f"{sentence['en']}\n{sentence['ja']}") else: self.text_edit.append("Error fetching information") self.scroll_to_top() def function5(self): print("Function 5") def search(self): search_text = self.findChild(QLineEdit, '').text() print(f"Searching for: {search_text}") def scroll_to_top(self): self.text_edit.verticalScrollBar().setValue(0) def highlight_common_words(self): common_words_file = r"C:\Users\jurad\Desktop\Capture2Text_v4.6.3_64bit\Bot Assets\output.txt" common_words = set() with open(common_words_file, 'r', encoding='utf-8') as f: for line in f: first_word = line.split()[0] common_words.add(first_word) for i in range(self.listbox.count()): item = self.listbox.item(i) word = item.text().split("(", 1)[0].strip() if word in common_words: item.setBackground(QColor(204, 255, 204)) # Minty green color if __name__ == "__main__": app = QApplication(sys.argv) window = MyWindow() window.show() sys.exit(app.exec_())