|
1 |
| -from PyQt6.QtCore import QUrl, Qt |
2 |
| -from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QLineEdit, QFileDialog, QPushButton |
3 |
| -from PyQt6.QtGui import QAction |
| 1 | +from PyQt6.QtCore import QUrl, Qt, QDir, QFileInfo |
| 2 | +from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QLineEdit, QFileDialog, QPushButton, QHBoxLayout, QTreeView, QSplitter, QLabel, QSizePolicy |
| 3 | +from PyQt6.QtGui import QAction, QStandardItemModel, QStandardItem, QIcon |
4 | 4 | from PyQt6.QtWebEngineWidgets import QWebEngineView
|
5 | 5 | from PyQt6.QtWebEngineCore import QWebEnginePage
|
| 6 | +import os |
| 7 | + |
| 8 | +class PDFFileSystemModel(QStandardItemModel): |
| 9 | + def __init__(self, parent=None): |
| 10 | + super().__init__(parent) |
| 11 | + self.setHorizontalHeaderLabels(["PDF Files and Folders"]) |
| 12 | + self.root_path = os.path.dirname(os.path.abspath(__file__)) |
| 13 | + self.current_path = self.root_path |
| 14 | + self.populate_model(self.invisibleRootItem(), self.current_path) |
| 15 | + |
| 16 | + def populate_model(self, parent_item, directory_path): |
| 17 | + parent_item.removeRows(0, parent_item.rowCount()) |
| 18 | + directory = QDir(directory_path) |
| 19 | + up_item = QStandardItem("") |
| 20 | + up_item.setData(os.path.dirname(directory_path), Qt.ItemDataRole.UserRole) |
| 21 | + up_item.setIcon(QIcon.fromTheme("go-up")) |
| 22 | + parent_item.appendRow(up_item) |
| 23 | + directories = directory.entryInfoList(QDir.Filter.Dirs | QDir.Filter.NoDotAndDotDot, QDir.SortFlag.Name) |
| 24 | + for dir_info in directories: |
| 25 | + dir_item = QStandardItem(dir_info.fileName()) |
| 26 | + dir_item.setData(dir_info.filePath(), Qt.ItemDataRole.UserRole) |
| 27 | + dir_item.setIcon(QIcon.fromTheme("folder")) |
| 28 | + parent_item.appendRow(dir_item) |
| 29 | + placeholder = QStandardItem("Loading...") |
| 30 | + dir_item.appendRow(placeholder) |
| 31 | + pdf_files = directory.entryInfoList(["*.pdf"], QDir.Filter.Files, QDir.SortFlag.Name) |
| 32 | + for file_info in pdf_files: |
| 33 | + file_item = QStandardItem(file_info.fileName()) |
| 34 | + file_item.setData(file_info.filePath(), Qt.ItemDataRole.UserRole) |
| 35 | + file_item.setIcon(QIcon.fromTheme("application-pdf")) |
| 36 | + parent_item.appendRow(file_item) |
| 37 | + |
| 38 | + def navigate_to(self, path): |
| 39 | + self.current_path = path |
| 40 | + self.populate_model(self.invisibleRootItem(), path) |
6 | 41 |
|
7 | 42 | class MainWindow(QMainWindow):
|
8 | 43 | def __init__(self):
|
9 |
| - super(QMainWindow, self).__init__() |
10 |
| - |
| 44 | + super().__init__() |
11 | 45 | self.setWindowTitle("PDF Viewer")
|
12 |
| - self.setGeometry(0, 28, 1000, 750) |
13 |
| - |
| 46 | + self.setGeometry(0, 28, 1200, 750) |
14 | 47 | self.central_widget = QWidget()
|
15 | 48 | self.setCentralWidget(self.central_widget)
|
16 |
| - |
17 |
| - self.layout = QVBoxLayout() |
18 |
| - self.central_widget.setLayout(self.layout) |
19 |
| - |
| 49 | + self.main_layout = QVBoxLayout(self.central_widget) |
| 50 | + self.tree_model = PDFFileSystemModel() |
| 51 | + self.path_label = QLabel() |
| 52 | + self.path_label.setText(self.tree_model.current_path) |
| 53 | + self.path_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) |
| 54 | + self.main_layout.addWidget(self.path_label) |
| 55 | + self.splitter = QSplitter(Qt.Orientation.Horizontal) |
| 56 | + self.main_layout.addWidget(self.splitter) |
| 57 | + self.nav_widget = QWidget() |
| 58 | + self.nav_layout = QVBoxLayout(self.nav_widget) |
| 59 | + self.tree_view = QTreeView() |
| 60 | + self.tree_view.setModel(self.tree_model) |
| 61 | + self.tree_view.setHeaderHidden(True) |
| 62 | + self.tree_view.clicked.connect(self.on_tree_clicked) |
| 63 | + self.tree_view.expanded.connect(self.on_tree_expanded) |
| 64 | + self.nav_layout.addWidget(self.tree_view) |
| 65 | + self.pdf_widget = QWidget() |
| 66 | + self.pdf_layout = QVBoxLayout(self.pdf_widget) |
20 | 67 | self.webView = QWebEngineView()
|
21 | 68 | self.webView.settings().setAttribute(self.webView.settings().WebAttribute.PluginsEnabled, True)
|
22 | 69 | self.webView.settings().setAttribute(self.webView.settings().WebAttribute.PdfViewerEnabled, True)
|
23 |
| - self.layout.addWidget(self.webView) |
24 |
| - |
25 |
| - self.search_input = QLineEdit(self) |
| 70 | + self.pdf_layout.addWidget(self.webView) |
| 71 | + self.search_input = QLineEdit() |
26 | 72 | self.search_input.setPlaceholderText("Enter text to search...")
|
27 | 73 | self.search_input.returnPressed.connect(lambda: self.search_text(self.search_input.text()))
|
28 |
| - self.layout.addWidget(self.search_input) |
29 |
| - |
| 74 | + self.pdf_layout.addWidget(self.search_input) |
| 75 | + self.splitter.addWidget(self.nav_widget) |
| 76 | + self.splitter.addWidget(self.pdf_widget) |
| 77 | + self.splitter.setSizes([250, 750]) |
30 | 78 | self.create_file_menu()
|
31 |
| - |
| 79 | + |
| 80 | + def on_tree_clicked(self, index): |
| 81 | + item = self.tree_model.itemFromIndex(index) |
| 82 | + file_path = item.data(Qt.ItemDataRole.UserRole) |
| 83 | + if item.text() == "": |
| 84 | + self.tree_model.navigate_to(file_path) |
| 85 | + self.path_label.setText(file_path) |
| 86 | + return |
| 87 | + if os.path.isdir(file_path): |
| 88 | + self.tree_model.navigate_to(file_path) |
| 89 | + self.path_label.setText(file_path) |
| 90 | + return |
| 91 | + if file_path and file_path.lower().endswith('.pdf'): |
| 92 | + self.path_label.setText(file_path) |
| 93 | + pdf_url = QUrl.fromLocalFile(file_path) |
| 94 | + pdf_url.setFragment("zoom=page-width") |
| 95 | + self.webView.setUrl(pdf_url) |
| 96 | + |
| 97 | + def on_tree_expanded(self, index): |
| 98 | + item = self.tree_model.itemFromIndex(index) |
| 99 | + file_path = item.data(Qt.ItemDataRole.UserRole) |
| 100 | + if item.rowCount() == 1 and item.child(0).text() == "Loading...": |
| 101 | + item.removeRow(0) |
| 102 | + self.tree_model.populate_model(item, file_path) |
| 103 | + |
32 | 104 | def create_file_menu(self):
|
33 | 105 | menubar = self.menuBar()
|
34 | 106 | file_menu = menubar.addMenu('Choose PDF')
|
35 |
| - |
36 | 107 | open_action = QAction('Open', self)
|
37 | 108 | open_action.triggered.connect(self.open_file_dialog)
|
38 | 109 | file_menu.addAction(open_action)
|
39 |
| - |
| 110 | + |
40 | 111 | def open_file_dialog(self):
|
41 | 112 | file_dialog = QFileDialog()
|
42 | 113 | filename, _ = file_dialog.getOpenFileName(self, "Open PDF", "", "PDF Files (*.pdf)")
|
43 | 114 | if filename:
|
44 |
| - self.webView.setUrl(QUrl.fromLocalFile(filename)) |
45 |
| - |
| 115 | + pdf_url = QUrl.fromLocalFile(filename) |
| 116 | + pdf_url.setFragment("zoom=page-width") |
| 117 | + self.webView.setUrl(pdf_url) |
| 118 | + |
46 | 119 | def search_text(self, text):
|
47 | 120 | flag = QWebEnginePage.FindFlag.FindCaseSensitively
|
48 | 121 | if text:
|
|
0 commit comments