Created
October 9, 2024 11:39
-
-
Save si3mshady/c320c415ad4928265e221ffaf1c6a0b3 to your computer and use it in GitHub Desktop.
This script is a PDF reader with translation features using PyQt5 and PyMuPDF, allowing users to double-click words for definitions and Spanish translations via the OpenAI API, requiring the `OPENAI_API_KEY` environment variable.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import os | |
import fitz # PyMuPDF | |
from PyQt5.QtWidgets import ( | |
QApplication, | |
QMainWindow, | |
QVBoxLayout, | |
QWidget, | |
QPushButton, | |
QFileDialog, | |
QScrollArea, | |
QLabel, | |
QMessageBox, | |
) | |
from PyQt5.QtGui import QImage, QPixmap | |
from PyQt5.QtCore import Qt | |
from openai import OpenAI | |
# Initialize the OpenAI client | |
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) | |
class PDFReader(QMainWindow): | |
def __init__(self): | |
super().__init__() | |
self.pdf_document = None | |
self.current_page = 0 | |
self.zoom_factor = 1.0 # Initial zoom factor | |
self.initUI() | |
def initUI(self): | |
self.setWindowTitle('PDF Reader with Translation') | |
self.setGeometry(100, 100, 800, 600) | |
central_widget = QWidget() | |
self.setCentralWidget(central_widget) | |
layout = QVBoxLayout() | |
central_widget.setLayout(layout) | |
self.scroll_area = QScrollArea() | |
self.scroll_area.setWidgetResizable(True) | |
layout.addWidget(self.scroll_area) | |
self.image_label = QLabel() | |
self.image_label.setAlignment(Qt.AlignCenter) | |
self.scroll_area.setWidget(self.image_label) | |
self.load_button = QPushButton('Load PDF') | |
self.load_button.clicked.connect(self.loadPDF) | |
layout.addWidget(self.load_button) | |
self.next_button = QPushButton('Next Page') | |
self.next_button.clicked.connect(self.nextPage) | |
layout.addWidget(self.next_button) | |
self.prev_button = QPushButton('Previous Page') | |
self.prev_button.clicked.connect(self.prevPage) | |
layout.addWidget(self.prev_button) | |
def resizeEvent(self, event): | |
if self.pdf_document: | |
available_width = self.scroll_area.viewport().width() | |
if self.pdf_document and self.current_page < len(self.pdf_document): | |
page = self.pdf_document[self.current_page] | |
page_width = page.rect.width # Fixed access to width | |
self.zoom_factor = available_width / page_width | |
self.loadPage() | |
super().resizeEvent(event) | |
def loadPDF(self): | |
file_name, _ = QFileDialog.getOpenFileName(self, "Open PDF file", "", "PDF files (*.pdf)") | |
if file_name: | |
try: | |
self.pdf_document = fitz.open(file_name) | |
self.current_page = 0 | |
self.loadPage() | |
except Exception as e: | |
print(f"Error loading PDF: {str(e)}") | |
def loadPage(self): | |
if self.pdf_document and 0 <= self.current_page < len(self.pdf_document): | |
page = self.pdf_document[self.current_page] | |
matrix = fitz.Matrix(self.zoom_factor, self.zoom_factor) | |
pix = page.get_pixmap(matrix=matrix) | |
img = QImage(pix.samples, pix.width, pix.height, pix.stride, QImage.Format_RGB888) | |
pixmap = QPixmap.fromImage(img) | |
self.image_label.setPixmap(pixmap) | |
self.setWindowTitle(f'PDF Reader - Page {self.current_page + 1} of {len(self.pdf_document)}') | |
self.image_label.setFixedSize(pixmap.size()) | |
def nextPage(self): | |
if self.pdf_document and self.current_page < len(self.pdf_document) - 1: | |
self.current_page += 1 | |
self.loadPage() | |
def prevPage(self): | |
if self.pdf_document and self.current_page > 0: | |
self.current_page -= 1 | |
self.loadPage() | |
def mousePressEvent(self, event): | |
if event.button() == Qt.LeftButton and self.pdf_document: | |
pos = self.image_label.mapFrom(self, event.pos()) | |
page = self.pdf_document[self.current_page] | |
pos_in_pdf = fitz.Point(pos.x() / self.zoom_factor, pos.y() / self.zoom_factor) | |
word = self.get_word_at_position(page, pos_in_pdf.x, pos_in_pdf.y) | |
if word: | |
definition_translation = self.getDefinitionAndTranslation(word) | |
self.showPopup(word, definition_translation) | |
def showPopup(self, word, definition_translation): | |
msg = QMessageBox() | |
msg.setWindowTitle(f"Translation of '{word}'") | |
msg.setText(f"Definition/Translation:\n\n{definition_translation}") | |
msg.setStandardButtons(QMessageBox.Ok) | |
msg.exec_() | |
def get_word_at_position(self, page, x, y): | |
words = page.get_text("words") | |
for word in words: | |
word_rect = fitz.Rect(word[:4]) # Get word bounding box | |
if word_rect.contains(fitz.Point(x, y)): # Check if point is inside word rectangle | |
return word[4] # Return the word text | |
return None | |
def getDefinitionAndTranslation(self, word): | |
try: | |
response = client.chat.completions.create( | |
model="gpt-3.5-turbo", | |
messages=[ | |
{"role": "system", "content": "You are a helpful assistant that provides brief definitions and Spanish translations."}, | |
{"role": "user", "content": f"Provide a brief definition and Spanish translation for the word: {word}"} | |
] | |
) | |
return response.choices[0].message.content | |
except Exception as e: | |
print(f"API Error: {str(e)}") | |
return f"Error: Unable to get definition and translation" | |
if __name__ == '__main__': | |
app = QApplication(sys.argv) | |
reader = PDFReader() | |
reader.show() | |
sys.exit(app.exec_()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment