Skip to content

Instantly share code, notes, and snippets.

@si3mshady
Created October 9, 2024 11:39
Show Gist options
  • Save si3mshady/c320c415ad4928265e221ffaf1c6a0b3 to your computer and use it in GitHub Desktop.
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.
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