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_())