src/eric7/PdfViewer/PdfViewerWindow.py

branch
pdf_viewer
changeset 9700
b74a13a382a8
parent 9699
92dcd34d54e4
child 9702
7c973954919d
equal deleted inserted replaced
9699:92dcd34d54e4 9700:b74a13a382a8
6 """ 6 """
7 Module implementing the PDF viewer main window. 7 Module implementing the PDF viewer main window.
8 """ 8 """
9 9
10 import contextlib 10 import contextlib
11 import math
12 import os 11 import os
13 import pathlib 12 import pathlib
14 13
15 from PyQt6.QtCore import Qt, pyqtSignal, QSize, pyqtSlot, QPointF 14 from PyQt6.QtCore import Qt, pyqtSignal, QSize, pyqtSlot, QPointF
16 from PyQt6.QtGui import QAction, QKeySequence 15 from PyQt6.QtGui import QAction, QKeySequence, QGuiApplication, QActionGroup
17 from PyQt6.QtPdf import QPdfDocument 16 from PyQt6.QtPdf import QPdfDocument
18 from PyQt6.QtPdfWidgets import QPdfView 17 from PyQt6.QtPdfWidgets import QPdfView
19 from PyQt6.QtWidgets import ( 18 from PyQt6.QtWidgets import (
20 QWhatsThis, QMenu, QTabWidget, QSplitter, QToolBar, QDialog 19 QWhatsThis, QMenu, QTabWidget, QSplitter, QToolBar, QDialog
21 ) 20 )
31 30
32 from .PdfPageSelector import PdfPageSelector 31 from .PdfPageSelector import PdfPageSelector
33 from .PdfZoomSelector import PdfZoomSelector 32 from .PdfZoomSelector import PdfZoomSelector
34 33
35 34
36 # TODO: make this an eric widget
37
38
39 class PdfViewerWindow(EricMainWindow): 35 class PdfViewerWindow(EricMainWindow):
40 """ 36 """
41 Class implementing the PDF viewer main window. 37 Class implementing the PDF viewer main window.
42 38
43 @signal viewerClosed() emitted after the window was requested to close 39 @signal viewerClosed() emitted after the window was requested to close
46 viewerClosed = pyqtSignal() 42 viewerClosed = pyqtSignal()
47 43
48 windows = [] 44 windows = []
49 45
50 maxMenuFilePathLen = 75 46 maxMenuFilePathLen = 75
51
52 ZoomMultiplier = math.sqrt(2.0)
53 47
54 def __init__(self, fileName="", parent=None, fromEric=False, project=None): 48 def __init__(self, fileName="", parent=None, fromEric=False, project=None):
55 """ 49 """
56 Constructor 50 Constructor
57 51
69 self.setObjectName("eric7_pdf_viewer") 63 self.setObjectName("eric7_pdf_viewer")
70 64
71 self.__fromEric = fromEric 65 self.__fromEric = fromEric
72 self.setWindowIcon(EricPixmapCache.getIcon("ericPdf")) 66 self.setWindowIcon(EricPixmapCache.getIcon("ericPdf"))
73 67
68 self.__screenResolution = (
69 QGuiApplication.primaryScreen().logicalDotsPerInch() / 72.0
70 )
71
74 if not self.__fromEric: 72 if not self.__fromEric:
75 self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet")) 73 self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet"))
76 74
77 self.__pdfDocument = QPdfDocument(self) 75 self.__pdfDocument = QPdfDocument(self)
78 76
79 self.__cw = QSplitter(Qt.Orientation.Horizontal, self) 77 self.__cw = QSplitter(Qt.Orientation.Horizontal, self)
80 self.__info = QTabWidget(self) 78 self.__info = QTabWidget(self)
81 self.__cw.addWidget(self.__info) 79 self.__cw.addWidget(self.__info)
82 self.__view = QPdfView(self) 80 self.__view = QPdfView(self)
83 self.__view.setDocument(self.__pdfDocument) 81 self.__view.setDocument(self.__pdfDocument)
84 self.__view.setPageMode(QPdfView.PageMode.MultiPage)
85 self.__cw.addWidget(self.__view) 82 self.__cw.addWidget(self.__view)
86 self.setCentralWidget(self.__cw) 83 self.setCentralWidget(self.__cw)
87 84
88 # create a few widgets needed in the toolbars 85 # create a few widgets needed in the toolbars
89 self.__pageSelector = PdfPageSelector(self) 86 self.__pageSelector = PdfPageSelector(self)
93 ) 90 )
94 self.__pageSelector.valueChanged.connect(self.__pageSelected) 91 self.__pageSelector.valueChanged.connect(self.__pageSelected)
95 self.__pageSelector.gotoPage.connect(self.__gotoPage) 92 self.__pageSelector.gotoPage.connect(self.__gotoPage)
96 93
97 self.__zoomSelector = PdfZoomSelector(self) 94 self.__zoomSelector = PdfZoomSelector(self)
98 self.__zoomSelector.zoomModeChanged.connect(self.__view.setZoomMode)
99 ##self.__zoomSelector.zoomModeChanged.connect(self.__setFocusToView)
100 self.__zoomSelector.zoomFactorChanged.connect(self.__view.setZoomFactor)
101 ##self.__zoomSelector.zoomFactorChanged.connect(self.__setFocusToView)
102 self.__zoomSelector.reset() 95 self.__zoomSelector.reset()
103 96
104 g = Preferences.getGeometry("PdfViewerGeometry") 97 g = Preferences.getGeometry("PdfViewerGeometry")
105 if g.isEmpty(): 98 if g.isEmpty():
106 s = QSize(1000, 1000) 99 s = QSize(1000, 1000)
112 self.__initActions() 105 self.__initActions()
113 self.__initMenus() 106 self.__initMenus()
114 self.__initToolbars() 107 self.__initToolbars()
115 self.__createStatusBar() 108 self.__createStatusBar()
116 109
110 self.__setDisplayMode() # needs to be done after actions have been created
111
117 self.__view.pageNavigator().backAvailableChanged.connect( 112 self.__view.pageNavigator().backAvailableChanged.connect(
118 self.backwardAct.setEnabled 113 self.backwardAct.setEnabled
119 ) 114 )
120 self.__view.pageNavigator().forwardAvailableChanged.connect( 115 self.__view.pageNavigator().forwardAvailableChanged.connect(
121 self.forwardAct.setEnabled 116 self.forwardAct.setEnabled
122 ) 117 )
118
119 self.__zoomSelector.zoomModeChanged.connect(self.__view.setZoomMode)
120 self.__zoomSelector.zoomModeChanged.connect(self.__zoomModeChanged)
121 self.__zoomSelector.zoomFactorChanged.connect(self.__view.setZoomFactor)
123 self.__view.zoomFactorChanged.connect(self.__zoomSelector.setZoomFactor) 122 self.__view.zoomFactorChanged.connect(self.__zoomSelector.setZoomFactor)
123 self.__view.zoomModeChanged.connect(self.__zoomSelector.setZoomMode)
124 124
125 PdfViewerWindow.windows.append(self) 125 PdfViewerWindow.windows.append(self)
126 126
127 self.__restoreViewerState() 127 self.__restoreViewerState()
128 128
388 388
389 def __initViewActions(self): 389 def __initViewActions(self):
390 """ 390 """
391 Private method to define the view related user interface actions. 391 Private method to define the view related user interface actions.
392 """ 392 """
393 # Page Width (checkable, exclusive)
394 # Whole Page (checkable, exclusive)
395 self.zoomInAct = EricAction( 393 self.zoomInAct = EricAction(
396 self.tr("Zoom in"), 394 self.tr("Zoom in"),
397 EricPixmapCache.getIcon("zoomIn"), 395 EricPixmapCache.getIcon("zoomIn"),
398 self.tr("Zoom &in"), 396 self.tr("Zoom &in"),
399 QKeySequence(self.tr("Ctrl++", "View|Zoom in")), 397 QKeySequence(self.tr("Ctrl++", "View|Zoom in")),
425 self, 423 self,
426 "pdfviewer_view_zoomreset", 424 "pdfviewer_view_zoomreset",
427 ) 425 )
428 self.zoomResetAct.triggered.connect(self.__zoomReset) 426 self.zoomResetAct.triggered.connect(self.__zoomReset)
429 self.__actions.append(self.zoomResetAct) 427 self.__actions.append(self.zoomResetAct)
428
429 self.zoomPageWidthAct = EricAction(
430 self.tr("Page Width"),
431 EricPixmapCache.getIcon("zoomFitWidth"),
432 self.tr("Page &Width"),
433 0,
434 0,
435 self,
436 "pdfviewer_view_zoompagewidth",
437 )
438 self.zoomPageWidthAct.triggered.connect(self.__zoomPageWidth)
439 self.zoomPageWidthAct.setCheckable(True)
440 self.__actions.append(self.zoomPageWidthAct)
441
442 self.zoomWholePageAct = EricAction(
443 self.tr("Whole Page"),
444 EricPixmapCache.getIcon("zoomFitPage"),
445 self.tr("Whole &Page"),
446 0,
447 0,
448 self,
449 "pdfviewer_view_zoomwholePage",
450 )
451 self.zoomWholePageAct.triggered.connect(self.__zoomWholePage)
452 self.zoomWholePageAct.setCheckable(True)
453 self.__actions.append(self.zoomWholePageAct)
454
455 ##self.__displayModeActGrp = QActionGroup(self)
456 ##
457 ##self.displayModeSingleAct = EricAction(
458 ##self.tr("Whole Page"),
459 ##EricPixmapCache.getIcon("zoomFitPage"),
460 ##self.tr("Whole &Page"),
461 ##0,
462 ##0,
463 ##self,
464 ##"pdfviewer_view_zoomwholePage",
465 ##)
466 ##self.displayModeSingleAct.triggered.connect(self.__zoomWholePage)
467 ##self.displayModeSingleAct.setCheckable(True)
468 ##self.__actions.append(self.displayModeSingleAct)
430 469
431 def __initHelpActions(self): 470 def __initHelpActions(self):
432 """ 471 """
433 Private method to create the Help actions. 472 Private method to create the Help actions.
434 """ 473 """
513 self.forwardAct.setEnabled(self.__view.pageNavigator().forwardAvailable()) 552 self.forwardAct.setEnabled(self.__view.pageNavigator().forwardAvailable())
514 553
515 self.zoomInAct.setEnabled(ready) 554 self.zoomInAct.setEnabled(ready)
516 self.zoomOutAct.setEnabled(ready) 555 self.zoomOutAct.setEnabled(ready)
517 self.zoomResetAct.setEnabled(ready) 556 self.zoomResetAct.setEnabled(ready)
557 self.zoomPageWidthAct.setEnabled(ready)
558 self.zoomWholePageAct.setEnabled(ready)
518 self.__zoomSelector.setEnabled(ready) 559 self.__zoomSelector.setEnabled(ready)
519 560
520 # TODO: not yet implemented 561 # TODO: not yet implemented
521 562
522 ##def setRecentPath(self, openPath): 563 ##def setRecentPath(self, openPath):
560 menu = mb.addMenu(self.tr("&View")) 601 menu = mb.addMenu(self.tr("&View"))
561 menu.setTearOffEnabled(True) 602 menu.setTearOffEnabled(True)
562 menu.addAction(self.zoomInAct) 603 menu.addAction(self.zoomInAct)
563 menu.addAction(self.zoomOutAct) 604 menu.addAction(self.zoomOutAct)
564 menu.addAction(self.zoomResetAct) 605 menu.addAction(self.zoomResetAct)
565 # TODO: not yet implemented 606 menu.addAction(self.zoomPageWidthAct)
607 menu.addAction(self.zoomWholePageAct)
608 menu.addSeparator()
609 modeMenu = menu.addMenu(self.tr("Display Mode"))
610 self.__singlePageAct = modeMenu.addAction(self.tr("Single Page"))
611 self.__singlePageAct.setCheckable(True)
612 self.__continuousPageAct = modeMenu.addAction(self.tr("Continuous"))
613 self.__continuousPageAct.setCheckable(True)
614 self.__displayModeActGrp = QActionGroup(self)
615 self.__displayModeActGrp.addAction(self.__singlePageAct)
616 self.__displayModeActGrp.addAction(self.__continuousPageAct)
617 modeMenu.triggered.connect(self.__displayModeSelected)
566 618
567 menu = mb.addMenu(self.tr("&Go To")) 619 menu = mb.addMenu(self.tr("&Go To"))
568 menu.setTearOffEnabled(True) 620 menu.setTearOffEnabled(True)
569 menu.addAction(self.previousPageAct) 621 menu.addAction(self.previousPageAct)
570 menu.addAction(self.nextPageAct) 622 menu.addAction(self.nextPageAct)
615 # TODO: not yet implemented 667 # TODO: not yet implemented
616 mainToolBar.addAction(self.zoomOutAct) 668 mainToolBar.addAction(self.zoomOutAct)
617 mainToolBar.addWidget(self.__zoomSelector) 669 mainToolBar.addWidget(self.__zoomSelector)
618 mainToolBar.addAction(self.zoomInAct) 670 mainToolBar.addAction(self.zoomInAct)
619 mainToolBar.addAction(self.zoomResetAct) 671 mainToolBar.addAction(self.zoomResetAct)
672 mainToolBar.addAction(self.zoomPageWidthAct)
673 mainToolBar.addAction(self.zoomWholePageAct)
620 674
621 self.addToolBar(mainToolBar) 675 self.addToolBar(mainToolBar)
622 self.addToolBarBreak() 676 self.addToolBarBreak()
623 677
624 def __createStatusBar(self): 678 def __createStatusBar(self):
1031 """ 1085 """
1032 Private slot to go forward in the view history. 1086 Private slot to go forward in the view history.
1033 """ 1087 """
1034 self.__view.pageNavigator().forward() 1088 self.__view.pageNavigator().forward()
1035 1089
1036 def __calculateZoomFactor(self): 1090 def __zoomInOut(self, zoomIn):
1037 if self.__view.ZoomMode == QPdfView.ZoomMode.FitToWidth: 1091 """
1038 pass 1092 Private method to zoom into or out of the view.
1093
1094 @param zoomIn flag indicating to zoom into the view
1095 @type bool
1096 """
1097 zoomFactor = self.__zoomFactorForMode(self.__view.zoomMode())
1098
1099 factors = list(PdfZoomSelector.ZoomValues)
1100 factors.append(self.__zoomFactorForMode(QPdfView.ZoomMode.FitInView))
1101 factors.append(self.__zoomFactorForMode(QPdfView.ZoomMode.FitToWidth))
1102 if zoomIn:
1103 factors.sort()
1104 if zoomFactor >= factors[-1]:
1105 return
1106 newIndex = next(x for x, val in enumerate(factors) if val > zoomFactor)
1039 else: 1107 else:
1108 factors.sort(reverse=True)
1109 if zoomFactor <= factors[-1]:
1110 return
1111 newIndex = next(x for x, val in enumerate(factors) if val < zoomFactor)
1112 newFactor = factors[newIndex]
1113 if newFactor == self.__zoomFactorForMode(QPdfView.ZoomMode.FitInView):
1114 self.__zoomWholePage(True)
1115 elif newFactor == self.__zoomFactorForMode(QPdfView.ZoomMode.FitToWidth):
1116 self.__zoomPageWidth(True)
1117 else:
1118 self.__view.setZoomFactor(newFactor)
1119 self.__zoomSelector.setZoomFactor(newFactor)
1120 self.__zoomModeChanged(QPdfView.ZoomMode.Custom)
1121
1122 @pyqtSlot()
1123 def __zoomIn(self):
1124 """
1125 Private slot to zoom into the view.
1126 """
1127 self.__zoomInOut(True)
1128
1129 @pyqtSlot()
1130 def __zoomOut(self):
1131 """
1132 Private slot to zoom out of the view.
1133 """
1134 self.__zoomInOut(False)
1135
1136 @pyqtSlot()
1137 def __zoomReset(self):
1138 """
1139 Private slot to reset the zoom factor of the view.
1140 """
1141 self.__view.setZoomFactor(1.0)
1142 self.__zoomSelector.setZoomFactor(1.0)
1143 self.__zoomModeChanged(QPdfView.ZoomMode.Custom)
1144
1145 @pyqtSlot(bool)
1146 def __zoomPageWidth(self, checked):
1147 """
1148 Private slot to fit the page width.
1149
1150 @param checked flag indicating the check state
1151 @type bool
1152 """
1153 if checked:
1154 self.__view.setZoomMode(QPdfView.ZoomMode.FitToWidth)
1155 self.zoomWholePageAct.setChecked(False)
1156
1157 @pyqtSlot(bool)
1158 def __zoomWholePage(self, checked):
1159 """
1160 Private slot to fit the page width.
1161
1162 @param checked flag indicating the check state
1163 @type bool
1164 """
1165 if checked:
1166 self.__view.setZoomMode(QPdfView.ZoomMode.FitInView)
1167 self.zoomPageWidthAct.setChecked(False)
1168
1169 @pyqtSlot(QPdfView.ZoomMode)
1170 def __zoomModeChanged(self, zoomMode):
1171 """
1172 Private slot to handle a change of the zoom mode.
1173
1174 @param zoomMode new zoom mode
1175 @type QPdfView.ZoomMode
1176 """
1177 self.zoomWholePageAct.setChecked(zoomMode == QPdfView.ZoomMode.FitInView)
1178 self.zoomPageWidthAct.setChecked(zoomMode == QPdfView.ZoomMode.FitToWidth)
1179
1180 def __zoomFactorForMode(self, zoomMode):
1181 """
1182 Private method to calculate the zoom factor iaw. the current zoom mode.
1183
1184 @param zoomMode zoom mode to get the zoom factor for
1185 @type QPdfView.ZoomMode
1186 @return zoom factor
1187 @rtype float
1188 """
1189 if zoomMode == QPdfView.ZoomMode.Custom:
1040 return self.__view.zoomFactor() 1190 return self.__view.zoomFactor()
1041 1191 else:
1042 @pyqtSlot() 1192 viewport = self.__view.viewport()
1043 def __zoomIn(self): 1193 curPage = self.__view.pageNavigator().currentPage()
1044 """ 1194 margins = self.__view.documentMargins()
1045 Private slot to zoom into the view. 1195 if zoomMode == QPdfView.ZoomMode.FitToWidth:
1046 """ 1196 pageSize = (
1047 self.__view.setZoomFactor( 1197 self.__pdfDocument.pagePointSize(curPage) * self.__screenResolution
1048 self.__view.zoomFactor() * PdfViewerWindow.ZoomMultiplier 1198 ).toSize()
1049 ) 1199 factor = (
1050 1200 viewport.width() - margins.left() - margins.right()
1051 @pyqtSlot() 1201 ) / pageSize.width()
1052 def __zoomOut(self): 1202 pageSize *= factor
1053 """ 1203 else:
1054 Private slot to zoom out of the view. 1204 # QPdfView.ZoomMode.FitInView
1055 """ 1205 viewportSize = viewport.size() + QSize(
1056 self.__view.setZoomFactor( 1206 -margins.left() - margins.right(), -self.__view.pageSpacing()
1057 self.__view.zoomFactor() / PdfViewerWindow.ZoomMultiplier 1207 )
1058 ) 1208 pageSize = (
1059 1209 self.__pdfDocument.pagePointSize(curPage) * self.__screenResolution
1060 @pyqtSlot() 1210 ).toSize()
1061 def __zoomReset(self): 1211 pageSize = pageSize.scaled(
1062 """ 1212 viewportSize, Qt.AspectRatioMode.KeepAspectRatio
1063 Private slot to reset the zoom factor of the view. 1213 )
1064 """ 1214 zoomFactor = pageSize.width() / (
1065 self.__view.setZoomFactor(1.0) 1215 self.__pdfDocument.pagePointSize(curPage) * self.__screenResolution
1216 ).width()
1217 return zoomFactor
1066 1218
1067 @pyqtSlot() 1219 @pyqtSlot()
1068 def __setFocusToView(self): 1220 def __setFocusToView(self):
1069 """ 1221 """
1070 Private slot to set the focus to the PDF document view. 1222 Private slot to set the focus to the PDF document view.
1071 """ 1223 """
1072 self.__view.setFocus(Qt.FocusReason.OtherFocusReason) 1224 self.__view.setFocus(Qt.FocusReason.OtherFocusReason)
1225
1226 @pyqtSlot(QAction)
1227 def __displayModeSelected(self, act):
1228 """
1229 Private slot to handle the selection of a display mode.
1230
1231 @param act reference to the triggering action
1232 @type QAction
1233 """
1234 if act is self.__singlePageAct:
1235 Preferences.setPdfViewer("PdfViewerDisplayMode", "single")
1236 else:
1237 Preferences.setPdfViewer("PdfViewerDisplayMode", "continuous")
1238 self.__setDisplayMode()
1239
1240 def __setDisplayMode(self):
1241 """
1242 Private method to set the display mode iaw. configuration.
1243 """
1244 if Preferences.getPdfViewer("PdfViewerDisplayMode") == "single":
1245 self.__view.setPageMode(QPdfView.PageMode.SinglePage)
1246 self.__singlePageAct.setChecked(True)
1247 else:
1248 self.__view.setPageMode(QPdfView.PageMode.MultiPage)
1249 self.__continuousPageAct.setChecked(True)
1250 return

eric ide

mercurial