Fri, 13 Jan 2023 18:20:54 +0100
Continued implementing a PDF viewer tool (page navigation).
--- a/eric7.epj Thu Jan 12 18:08:12 2023 +0100 +++ b/eric7.epj Fri Jan 13 18:20:54 2023 +0100 @@ -1314,6 +1314,7 @@ "src/eric7/Network/IRC/IrcWidget.py", "src/eric7/Network/IRC/__init__.py", "src/eric7/Network/__init__.py", + "src/eric7/PdfViewer/PdfPageSelector.py", "src/eric7/PdfViewer/PdfViewerWindow.py", "src/eric7/PdfViewer/__init__.py", "src/eric7/PipInterface/Pip.py", @@ -2365,6 +2366,7 @@ "src/eric7/eric7_ide.py", "src/eric7/eric7_ide.pyw", "src/eric7/eric7_pdf.py", + "src/eric7/eric7_pdf.pyw", "src/eric7/eric7_plugininstall.py", "src/eric7/eric7_plugininstall.pyw", "src/eric7/eric7_pluginrepository.py",
--- a/src/eric7/HexEdit/HexEditMainWindow.py Thu Jan 12 18:08:12 2023 +0100 +++ b/src/eric7/HexEdit/HexEditMainWindow.py Fri Jan 13 18:20:54 2023 +0100 @@ -41,7 +41,7 @@ """ Class implementing the hex editor main window. - @signal editorClosed() emitted after the window was requested to close down + @signal editorClosed() emitted after the window was requested to close """ editorClosed = pyqtSignal() @@ -108,7 +108,7 @@ self.__initToolbars() self.__createStatusBar() - self.__class__.windows.append(self) + HexEditMainWindow.windows.append(self) state = Preferences.getHexEditor("HexEditorState") self.restoreState(state) @@ -1028,8 +1028,8 @@ Preferences.setGeometry("HexEditorGeometry", self.saveGeometry()) with contextlib.suppress(ValueError): - if self.__fromEric or len(self.__class__.windows) > 1: - del self.__class__.windows[self.__class__.windows.index(self)] + if self.__fromEric or len(HexEditMainWindow.windows) > 1: + HexEditMainWindow.windows.remove(self) if not self.__fromEric: Preferences.syncPreferences() @@ -1323,7 +1323,7 @@ """ Private slot to close all other windows. """ - for win in self.__class__.windows[:]: + for win in HexEditMainWindow.windows[:]: if win != self: win.close()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/PdfViewer/PdfPageSelector.py Fri Jan 13 18:20:54 2023 +0100 @@ -0,0 +1,210 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a widget to select a PDF page to be shown. +""" + +import contextlib + +from PyQt6.QtCore import Qt, pyqtSlot, pyqtSignal +from PyQt6.QtGui import QIntValidator +from PyQt6.QtPdf import QPdfDocument +from PyQt6.QtWidgets import ( + QToolButton, QHBoxLayout, QWidget, QLabel, QLineEdit +) + +from eric7.EricGui import EricPixmapCache + + +class PdfPageSelector(QWidget): + """ + Class implementing a widget to select a PDF page to be shown. + """ + + valueChanged = pyqtSignal(int) + gotoPage = pyqtSignal() + + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + + self.__document = None + + self.__prevButton = QToolButton(self) + self.__prevButton.setIcon(EricPixmapCache.getIcon("1uparrow")) + + self.__nextButton = QToolButton(self) + self.__nextButton.setIcon(EricPixmapCache.getIcon("1downarrow")) + + self.__pageButton = QToolButton() + self.__pageButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextOnly) + + self.__pageEntry = QLineEdit() + self.__pageEntry.setMaxLength(10) + self.__pageEntry.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.__pageLabel = QLabel() + + self.__layout = QHBoxLayout() + self.__layout.addWidget(self.__prevButton) + self.__layout.addWidget(self.__pageEntry) + self.__layout.addWidget(self.__pageLabel) + self.__layout.addWidget(QLabel(self.tr("of"))) + self.__layout.addWidget(self.__pageButton) + self.__layout.addWidget(self.__nextButton) + + self.setLayout(self.__layout) + + # Setup signal/slot connections + self.__prevButton.clicked.connect(self.__decrement) + self.__nextButton.clicked.connect(self.__increment) + self.__pageButton.clicked.connect(self.__pageButtonTriggered) + self.__pageEntry.editingFinished.connect(self.__pageEntered) + + self.__initialize() + + def __initialize(self): + """ + Private method to initialize some internal state. + """ + self.__value = -1 + self.__minimum = 0 + self.__maximum = 0 + + self.__prevButton.setEnabled(False) + self.__nextButton.setEnabled(False) + self.__pageEntry.clear() + self.__pageLabel.clear() + self.__pageButton.setText(" ") + + self.setEnabled(False) + + def setDocument(self, document): + """ + Public method to set a reference to the associated PDF document. + + @param document reference to the associated PDF document + @type QPdfDocument + """ + self.__document = document + self.__document.statusChanged.connect(self.__documentStatusChanged) + + @pyqtSlot(int) + def setValue(self, value): + """ + Public slot to set the value. + + Note: value is 0 based. + + @param value value to be set + @type int + """ + if value != self.__value: + with contextlib.suppress(RuntimeError): + self.__pageEntry.setText(self.__document.pageLabel(value)) + self.__pageLabel.setText(str(value + 1)) + + self.__value = value + + self.__prevButton.setEnabled(value > self.__minimum) + self.__nextButton.setEnabled(value < self.__maximum) + + self.valueChanged.emit(value) + + def value(self): + """ + Public method to get the current value. + + @return current value + @rtype int + """ + return self.__value + + def setMaximum(self, max): + """ + Public method to set the maximum value. + + Note: max is 0 based. + + @param max maximum value to be set + @type int + """ + self.__maximum = max + self.__nextButton.setEnabled(self.__value < self.__maximum) + self.__pageButton.setText(str(max + 1)) + + @pyqtSlot() + def __pageEntered(self): + """ + Private slot to handle the entering of a page value. + """ + model = self.__document.pageModel() + start = model.index(0) + indices = model.match( + start, QPdfDocument.PageModelRole.Label.value, self.__pageEntry.text() + ) + if indices: + self.setValue(indices[0].row()) + else: + # reset + blocked = self.__pageEntry.blockSignals(True) + self.__pageEntry.setText(self.__document.pageLabel(self.__value)) + self.__pageEntry.blockSignals(blocked) + + @pyqtSlot() + def __decrement(self): + """ + Private slot to decrement the current value. + """ + if self.__value > self.__minimum: + self.setValue(self.__value - 1) + + @pyqtSlot() + def __increment(self): + """ + Private slot to increment the current value. + """ + if self.__value < self.__maximum: + self.setValue(self.__value + 1) + + @pyqtSlot() + def __pageButtonTriggered(self): + """ + Private slot to handle the page button trigger. + """ + self.gotoPage.emit() + + @pyqtSlot(QPdfDocument.Status) + def __documentStatusChanged(self, status): + """ + Private slot to handle a change of the document status. + + @param status current document status + @type QPdfDocument.Status + """ + self.setEnabled(status == QPdfDocument.Status.Ready) + if status == QPdfDocument.Status.Ready: + numericalEntry = True + # test the first page + try: + _ = int(self.__document.pageLabel(0)) + except ValueError: + numericalEntry = False + # test the last page + try: + _ = int(self.__document.pageLabel(self.__document.pageCount() - 1)) + except ValueError: + numericalEntry = False + self.__pageEntry.setValidator( + QIntValidator(1, 99999) if numericalEntry else None + ) + self.__pageLabel.setVisible(not numericalEntry) + elif status == QPdfDocument.Status.Null: + self.__initialize()
--- a/src/eric7/PdfViewer/PdfViewerWindow.py Thu Jan 12 18:08:12 2023 +0100 +++ b/src/eric7/PdfViewer/PdfViewerWindow.py Fri Jan 13 18:20:54 2023 +0100 @@ -7,6 +7,7 @@ Module implementing the PDF viewer main window. """ +import contextlib import os import pathlib @@ -15,7 +16,7 @@ from PyQt6.QtPdf import QPdfDocument from PyQt6.QtPdfWidgets import QPdfView from PyQt6.QtWidgets import ( - QWhatsThis, QMenu, QTabWidget, QSplitter, QSpinBox + QWhatsThis, QMenu, QTabWidget, QSplitter, QSpinBox, QAbstractSpinBox ) from eric7 import Preferences @@ -26,15 +27,19 @@ from eric7.Globals import recentNamePdfFiles from eric7.SystemUtilities import FileSystemUtilities +from .PdfPageSelector import PdfPageSelector + class PdfViewerWindow(EricMainWindow): """ Class implementing the PDF viewer main window. - @signal editorClosed() emitted after the window was requested to close down + @signal viewerClosed() emitted after the window was requested to close """ - editorClosed = pyqtSignal() + viewerClosed = pyqtSignal() + + windows = [] maxMenuFilePathLen = 75 @@ -63,15 +68,24 @@ self.__pdfDocument = QPdfDocument(self) - # TODO: insert central widget here self.__cw = QSplitter(Qt.Orientation.Horizontal, self) self.__info = QTabWidget(self) self.__cw.addWidget(self.__info) self.__view = QPdfView(self) self.__view.setDocument(self.__pdfDocument) + self.__view.setPageMode(QPdfView.PageMode.MultiPage) self.__cw.addWidget(self.__view) self.setCentralWidget(self.__cw) + # create a few widgets needed in the toolbars + self.__pageSelector = PdfPageSelector() + self.__pageSelector.setDocument(self.__pdfDocument) + self.__view.pageNavigator().currentPageChanged.connect( + self.__pageSelector.setValue + ) + self.__pageSelector.valueChanged.connect(self.__pageSelected) + self.__pageSelector.gotoPage.connect(self.__gotoPage) + g = Preferences.getGeometry("PdfViewerGeometry") if g.isEmpty(): s = QSize(1000, 1000) @@ -85,8 +99,21 @@ self.__initToolbars() self.__createStatusBar() + self.__view.pageNavigator().backAvailableChanged.connect( + self.backwardAct.setEnabled + ) + self.__view.pageNavigator().forwardAvailableChanged.connect( + self.forwardAct.setEnabled + ) + + PdfViewerWindow.windows.append(self) + state = Preferences.getPdfViewer("PdfViewerState") self.restoreState(state) + splitterState = Preferences.getPdfViewer("PdfViewerSplitterState") + self.__cw.restoreState(splitterState) + + self.__checkActions() self.__project = project self.__lastOpenPath = "" @@ -99,8 +126,6 @@ if fileName: self.__loadPdfFile(fileName) - self.__checkActions() - def __initActions(self): """ Private method to define the user interface actions. @@ -109,13 +134,126 @@ self.__actions = [] self.__initFileActions() + self.__initGotoActions() + self.__initViewActions() self.__initHelpActions() def __initFileActions(self): """ Private method to define the file related user interface actions. """ - # TODO: not yet implemented + self.newWindowAct = EricAction( + self.tr("New Window"), + EricPixmapCache.getIcon("newWindow"), + self.tr("New &Window"), + QKeySequence(self.tr("Ctrl+Shift+N", "File|New Window")), + 0, + self, + "pdfviewer_file_new_window", + ) + self.newWindowAct.setStatusTip( + self.tr("Open a PDF file in a new PDF Viewer window") + ) + self.newWindowAct.setWhatsThis( + self.tr( + """<b>New Window</b>""" + """<p>This opens a PDF file in a new PDF Viewer window. It pops up""" + """ a file selection dialog.</p>""" + ) + ) + self.newWindowAct.triggered.connect(self.__openPdfFileNewWindow) + self.__actions.append(self.newWindowAct) + + self.openAct = EricAction( + self.tr("Open"), + EricPixmapCache.getIcon("open"), + self.tr("&Open..."), + QKeySequence(self.tr("Ctrl+O", "File|Open")), + 0, + self, + "pdfviewer_file_open", + ) + self.openAct.setStatusTip(self.tr("Open a PDF file for viewing")) + self.openAct.setWhatsThis( + self.tr( + """<b>Open</b>""" + """<p>This opens a PDF file for viewing. It pops up a file""" + """ selection dialog.</p>""" + ) + ) + self.openAct.triggered.connect(self.__openPdfFile) + self.__actions.append(self.openAct) + + self.reloadAct = EricAction( + self.tr("Reload"), + EricPixmapCache.getIcon("reload"), + self.tr("&Reload"), + QKeySequence("F5"), + 0, + self, + "pdfviewer_file_reload", + ) + self.reloadAct.setStatusTip(self.tr("Reload the current PDF document")) + self.reloadAct.triggered.connect(self.__reload) + self.__actions.append(self.reloadAct) + + # TODO: maybe this will be a tab of the side widget + self.propertiesAct = EricAction( + self.tr("Properties"), + EricPixmapCache.getIcon("documentProperties"), + self.tr("&Properties..."), + QKeySequence(self.tr("Alt+Return")), + 0, + self, + "pdfviewer_file_properties", + ) + self.propertiesAct.setStatusTip(self.tr("Show the document properties")) + self.propertiesAct.setWhatsThis( + self.tr( + """<b>Properties</b><p>Opens a dialog showing the document""" + """ properties.</p>""" + ) + ) + self.propertiesAct.triggered.connect(self.__showDocumentProperties) + self.__actions.append(self.propertiesAct) + + self.closeAct = EricAction( + self.tr("Close"), + EricPixmapCache.getIcon("close"), + self.tr("&Close"), + QKeySequence(self.tr("Ctrl+W", "File|Close")), + 0, + self, + "pdfviewer_file_close", + ) + self.closeAct.setStatusTip(self.tr("Close the current PDF Viewer window")) + self.closeAct.triggered.connect(self.close) + self.__actions.append(self.closeAct) + + self.closeAllAct = EricAction( + self.tr("Close All"), + self.tr("Close &All"), + 0, + 0, + self, + "pdfviewer_file_close_all", + ) + self.closeAllAct.setStatusTip(self.tr("Close all PDF Viewer windows")) + self.closeAllAct.triggered.connect(self.__closeAll) + self.__actions.append(self.closeAllAct) + + self.closeOthersAct = EricAction( + self.tr("Close Others"), + self.tr("Close Others"), + 0, + 0, + self, + "pdfviewer_file_close_others", + ) + self.closeOthersAct.setStatusTip(self.tr("Close all other PDF Viewer windows")) + self.closeOthersAct.triggered.connect(self.__closeOthers) + self.__actions.append(self.closeOthersAct) + self.exitAct = EricAction( self.tr("Quit"), EricPixmapCache.getIcon("exit"), @@ -126,9 +264,111 @@ "pdfviewer_file_quit", ) self.exitAct.setStatusTip(self.tr("Quit the PDF Viewer")) - self.exitAct.setWhatsThis(self.tr("""<b>Quit</b><p>Quit the PDF Viewer.</p>""")) + if not self.__fromEric: + self.exitAct.triggered.connect(self.__closeAll) self.__actions.append(self.exitAct) + def __initGotoActions(self): + """ + Private method to define the navigation related user interface actions. + """ + # TODO: Goto page (goto dialog) (Ctrl+G) + self.previousPageAct = EricAction( + self.tr("Previous Page"), + EricPixmapCache.getIcon("1leftarrow"), + self.tr("&Previous Page"), + 0, + 0, + self, + "pdfviewer_goto_previous", + ) + self.previousPageAct.setStatusTip(self.tr("Go to the previous page")) + self.previousPageAct.triggered.connect(self.__previousPage) + self.__actions.append(self.previousPageAct) + + self.nextPageAct = EricAction( + self.tr("Next Page"), + EricPixmapCache.getIcon("1rightarrow"), + self.tr("&Next Page"), + 0, + 0, + self, + "pdfviewer_goto_next", + ) + self.nextPageAct.setStatusTip(self.tr("Go to the next page")) + self.nextPageAct.triggered.connect(self.__nextPage) + self.__actions.append(self.nextPageAct) + + self.startDocumentAct = EricAction( + self.tr("Start of Document"), + EricPixmapCache.getIcon("gotoFirst"), + self.tr("&Start of Document"), + QKeySequence(self.tr("Ctrl+Home", "Goto|Start")), + 0, + self, + "pdfviewer_goto_start", + ) + self.startDocumentAct.setStatusTip( + self.tr("Go to the first page of the document") + ) + self.startDocumentAct.triggered.connect(self.__startDocument) + self.__actions.append(self.startDocumentAct) + + self.endDocumentAct = EricAction( + self.tr("End of Document"), + EricPixmapCache.getIcon("gotoLast"), + self.tr("&End of Document"), + QKeySequence(self.tr("Ctrl+End", "Goto|End")), + 0, + self, + "pdfviewer_goto_end", + ) + self.endDocumentAct.setStatusTip( + self.tr("Go to the last page of the document") + ) + self.endDocumentAct.triggered.connect(self.__endDocument) + self.__actions.append(self.endDocumentAct) + + self.backwardAct = EricAction( + self.tr("Back"), + EricPixmapCache.getIcon("back"), + self.tr("&Back"), + QKeySequence(self.tr("Alt+Shift+Left", "Goto|Back")), + 0, + self, + "pdfviewer_goto_back", + ) + self.backwardAct.setStatusTip( + self.tr("Go back in the view history") + ) + self.backwardAct.triggered.connect(self.__backInHistory) + self.__actions.append(self.backwardAct) + + self.forwardAct = EricAction( + self.tr("Forward"), + EricPixmapCache.getIcon("forward"), + self.tr("&Forward"), + QKeySequence(self.tr("Alt+Shift+Right", "Goto|Forward")), + 0, + self, + "pdfviewer_goto_forward", + ) + self.forwardAct.setStatusTip( + self.tr("Go forward in the view history") + ) + self.forwardAct.triggered.connect(self.__forwardInHistory) + self.__actions.append(self.forwardAct) + + def __initViewActions(self): + """ + Private method to define the view related user interface actions. + """ + # Zoom in (Ctrl++) + # Zoom out (Ctrl+-) + # Zoom reset (Ctrl+0) + # Page Width (checkable, exclusive) + # Whole Page (checkable, exclusive) + def __initHelpActions(self): """ Private method to create the Help actions. @@ -194,8 +434,31 @@ """ Private slot to check some actions for their enable/disable status. """ + self.reloadAct.setEnabled( + self.__pdfDocument.status() == QPdfDocument.Status.Ready + ) + + curPage = self.__view.pageNavigator().currentPage() + self.previousPageAct.setEnabled(curPage > 0) + self.nextPageAct.setEnabled(curPage < self.__pdfDocument.pageCount() - 1) + self.startDocumentAct.setEnabled(curPage != 0) + self.endDocumentAct.setEnabled(curPage != self.__pdfDocument.pageCount() - 1) + + self.backwardAct.setEnabled(self.__view.pageNavigator().backAvailable()) + self.forwardAct.setEnabled(self.__view.pageNavigator().forwardAvailable()) + # TODO: not yet implemented + ##def setRecentPath(self, openPath): + ##""" + ##Public method to set the last open path. +## + ##@param openPath least recently used open path + ##@type str + ##""" + ##if openPath: + ##self.__lastOpenPath = openPath + def __initMenus(self): """ Private method to create the menus. @@ -205,7 +468,36 @@ menu = mb.addMenu(self.tr("&File")) menu.setTearOffEnabled(True) self.__recentMenu = QMenu(self.tr("Open &Recent Files"), menu) + menu.addAction(self.newWindowAct) + menu.addAction(self.openAct) + self.__menuRecentAct = menu.addMenu(self.__recentMenu) + menu.addSeparator() + menu.addAction(self.reloadAct) + menu.addSeparator() + menu.addAction(self.propertiesAct) + menu.addSeparator() + menu.addAction(self.closeAct) + menu.addAction(self.closeOthersAct) + if self.__fromEric: + menu.addAction(self.closeAllAct) + else: + menu.addSeparator() + menu.addAction(self.exitAct) + menu.aboutToShow.connect(self.__showFileMenu) + self.__recentMenu.aboutToShow.connect(self.__showRecentMenu) + self.__recentMenu.triggered.connect(self.__openRecentPdfFile) + menu = mb.addMenu(self.tr("&Go To")) + menu.setTearOffEnabled(True) + menu.addAction(self.previousPageAct) + menu.addAction(self.nextPageAct) + menu.addSeparator() + menu.addAction(self.startDocumentAct) + menu.addAction(self.endDocumentAct) + menu.addSeparator() + menu.addAction(self.backwardAct) + menu.addAction(self.forwardAct) + menu.addSeparator() # TODO: not yet implemented mb.addSeparator() @@ -220,15 +512,23 @@ """ Private method to create the toolbars. """ - # create a few widgets needed in the toolbars - self.__pageSelector = QSpinBox(self) - filetb = self.addToolBar(self.tr("File")) filetb.setObjectName("FileToolBar") - # TODO: not yet implemented + filetb.addAction(self.newWindowAct) + filetb.addAction(self.openAct) + filetb.addSeparator() + filetb.addAction(self.closeAct) if not self.__fromEric: filetb.addAction(self.exitAct) + gototb = self.addToolBar(self.tr("Goto")) + gototb.setObjectName("GotoToolBar") + gototb.addAction(self.startDocumentAct) + gototb.addWidget(self.__pageSelector) + gototb.addAction(self.endDocumentAct) + + viewtb = self.addToolBar(self.tr("View")) + viewtb.setObjectName("ViewToolBar") # TODO: not yet implemented helptb = self.addToolBar(self.tr("Help")) @@ -253,16 +553,22 @@ """ state = self.saveState() Preferences.setPdfViewer("PdfViewerState", state) + splitterState = self.__cw.saveState() + Preferences.setPdfViewer("PdfViewerSplitterState", splitterState) Preferences.setGeometry("PdfViewerGeometry", self.saveGeometry()) + with contextlib.suppress(ValueError): + if self.__fromEric or len(PdfViewerWindow.windows) > 1: + PdfViewerWindow.windows.remove(self) + if not self.__fromEric: Preferences.syncPreferences() self.__saveRecent() evt.accept() - self.editorClosed.emit() + self.viewerClosed.emit() def __setViewerTitle(self, title): """ @@ -309,7 +615,6 @@ @param fileName path of the PDF file to load @type str """ - # TODO: not yet implemented err = self.__pdfDocument.load(fileName) if err != QPdfDocument.Error.None_: EricMessageBox.critical( @@ -330,7 +635,16 @@ self.__pageSelected(0) self.__pageSelector.setMaximum(self.__pdfDocument.pageCount() - 1) + self.__pageSelector.setValue(0) + @pyqtSlot() + def __reload(self): + """ + Private slot to reload the current PDF document. + """ + self.__loadPdfFile(self.__fileName) + + @pyqtSlot() def __openPdfFile(self): """ Private slot to open a PDF file. @@ -351,11 +665,54 @@ if fileName: self.__loadPdfFile(fileName) - self.__checkActions() + @pyqtSlot() + def __openPdfFileNewWindow(self): + """ + Private slot called to open a PDF file in new viewer window. + """ + if ( + not self.__lastOpenPath + and self.__project is not None + and self.__project.isOpen() + ): + self.__lastOpenPath = self.__project.getProjectPath() + fileName = EricFileDialog.getOpenFileName( + self, + self.tr("Open PDF File"), + self.__lastOpenPath, + self.tr("PDF Files (*.pdf);;All Files (*)"), + ) + if fileName: + viewer = PdfViewerWindow( + fileName=fileName, + parent=self.parent(), + fromEric=self.__fromEric, + project=self.__project, + ) + viewer.show() + + @pyqtSlot() + def __closeAll(self): + """ + Private slot to close all windows. + """ + self.__closeOthers() + self.close() + + @pyqtSlot() + def __closeOthers(self): + """ + Private slot to close all other windows. + """ + for win in PdfViewerWindow.windows[:]: + if win != self: + win.close() + + @pyqtSlot(int) def __pageSelected(self, page): """ - Private method to navigate to the given page. + Private slot to navigate to the given page. @param page index of the page to be shown @type int @@ -363,6 +720,8 @@ nav = self.__view.pageNavigator() nav.jump(page, QPointF(), nav.currentZoom()) + self.__checkActions() + def __setCurrentFile(self, fileName): """ Private method to register the file name of the current file. @@ -374,17 +733,7 @@ # insert filename into list of recently opened files self.__addToRecentList(fileName) - def __strippedName(self, fullFileName): - """ - Private method to return the filename part of the given path. - - @param fullFileName full pathname of the given file - @type str - @return filename part - @rtype str - """ - return pathlib.Path(fullFileName).name - + @pyqtSlot() def __about(self): """ Private slot to show a little About message. @@ -397,18 +746,21 @@ ), ) + @pyqtSlot() def __aboutQt(self): """ Private slot to handle the About Qt dialog. """ EricMessageBox.aboutQt(self, "eric PDF Viewer") + @pyqtSlot() def __whatsThis(self): """ Private slot called in to enter Whats This mode. """ QWhatsThis.enterWhatsThisMode() + @pyqtSlot() def __showPreferences(self): """ Private slot to set the preferences. @@ -461,9 +813,7 @@ @type QAction """ fileName = act.data() - if fileName and self.__maybeSave(): - self.__loadPdfFile(fileName) - self.__checkActions() + self.__loadPdfFile(fileName) @pyqtSlot() def __clearRecent(self): @@ -506,3 +856,63 @@ if len(self.__recent) > maxRecent: self.__recent = self.__recent[:maxRecent] self.__saveRecent() + + @pyqtSlot() + def __showDocumentProperties(self): + """ + Private slot to open a dialog showing the document properties. + """ + # TODO: not yet implemented + + @pyqtSlot() + def __gotoPage(self): + """ + Private slot to show a dialog to select a page to jump to. + """ + # TODO: not yet implemented + + @pyqtSlot() + def __previousPage(self): + """ + Private slot to go to the previous page. + """ + curPage = self.__view.pageNavigator().currentPage() + if curPage > 0: + self.__pageSelected(curPage - 1) + + @pyqtSlot() + def __nextPage(self): + """ + Private slot to go to the next page. + """ + curPage = self.__view.pageNavigator().currentPage() + if curPage < self.__pdfDocument.pageCount() - 1: + self.__pageSelected(curPage + 1) + + @pyqtSlot() + def __startDocument(self): + """ + Private slot to go to the first page of the document. + """ + self.__pageSelected(0) + + @pyqtSlot() + def __endDocument(self): + """ + Private slot to go to the last page of the document. + """ + self.__pageSelected(self.__pdfDocument.pageCount() - 1) + + @pyqtSlot() + def __backInHistory(self): + """ + Private slot to go back in the view history. + """ + self.__view.pageNavigator().back() + + @pyqtSlot() + def __forwardInHistory(self): + """ + Private slot to go forward in the view history. + """ + self.__view.pageNavigator().forward()
--- a/src/eric7/Preferences/__init__.py Thu Jan 12 18:08:12 2023 +0100 +++ b/src/eric7/Preferences/__init__.py Fri Jan 13 18:20:54 2023 +0100 @@ -1633,6 +1633,7 @@ # defaults for Hex Editor pdfViewerDefaults = { "PdfViewerState": QByteArray(), + "PdfViewerSplitterState": QByteArray(), "RecentNumber": 9, }
--- a/src/eric7/eric7_pdf.py Thu Jan 12 18:08:12 2023 +0100 +++ b/src/eric7/eric7_pdf.py Fri Jan 13 18:20:54 2023 +0100 @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright (c) 2016 - 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> # """
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/eric7_pdf.pyw Fri Jan 13 18:20:54 2023 +0100 @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the Windows entry point. +""" + +if __name__ == "__main__": + from eric7_pdf import main + + main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/documentProperties.svg Fri Jan 13 18:20:54 2023 +0100 @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg6" + sodipodi:docname="documentProperties.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview8" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg6" /> + <defs + id="defs3051"> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#eff0f1; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none;stroke-width:1.25" + d="M 1,1 V 21 H 2.25 21 V 19.75 1 H 19.75 2.25 1 m 1.25,5 h 17.5 V 19.75 H 2.25 V 6 M 3.5,7.25 V 18.5 H 9.75 V 7.25 H 3.5 M 11,8.5 v 1.25 h 7.5 V 8.5 H 11 m 0,3.75 v 1.25 h 5 V 12.25 H 11 M 11,16 v 1.25 h 2.5 V 16 H 11" + class="ColorScheme-Text" + id="path4" /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/gotoFirst.svg Fri Jan 13 18:20:54 2023 +0100 @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg9" + sodipodi:docname="gotoFirst.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs13" /> + <sodipodi:namedview + id="namedview11" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg9" /> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#eff0f1; + } + </style> + <g + class="ColorScheme-Text" + fill="currentColor" + id="g7" + transform="matrix(1.2666667,0,0,1.25,-2.3000001,-2.75)"> + <path + d="M 3,19 H 4 V 3 H 3 Z m 6.293,-8 8,8 L 18,18.293 10.707,11 18,3.707 17.293,3 Z" + id="path3" /> + <path + d="m 4.293,11 8,8 L 13,18.293 5.707,11 13,3.707 12.293,3 Z" + id="path5" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/gotoLast.svg Fri Jan 13 18:20:54 2023 +0100 @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg9" + sodipodi:docname="gotoLast.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs13" /> + <sodipodi:namedview + id="namedview11" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg9" /> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#eff0f1; + } + </style> + <g + class="ColorScheme-Text" + fill="currentColor" + id="g7" + transform="matrix(1.2666667,0,0,1.25,-3.5666668,-2.75)"> + <path + d="m 19,3 h -1 v 16 h 1 z m -6.293,8 -8,-8 L 4,3.707 11.293,11 4,18.293 4.707,19 Z" + id="path3" /> + <path + d="M 17.707,11 9.707,3 9,3.707 16.293,11 9,18.293 9.707,19 Z" + id="path5" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/documentProperties.svg Fri Jan 13 18:20:54 2023 +0100 @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg6" + sodipodi:docname="documentProperties.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview8" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg6" /> + <defs + id="defs3051"> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none;stroke-width:1.25" + d="M 1,1 V 21 H 2.25 21 V 19.75 1 H 19.75 2.25 1 m 1.25,5 h 17.5 V 19.75 H 2.25 V 6 M 3.5,7.25 V 18.5 H 9.75 V 7.25 H 3.5 M 11,8.5 v 1.25 h 7.5 V 8.5 H 11 m 0,3.75 v 1.25 h 5 V 12.25 H 11 M 11,16 v 1.25 h 2.5 V 16 H 11" + class="ColorScheme-Text" + id="path4" /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/gotoFirst.svg Fri Jan 13 18:20:54 2023 +0100 @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg9" + sodipodi:docname="gotoFirst.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs13" /> + <sodipodi:namedview + id="namedview11" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg9" /> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + <g + class="ColorScheme-Text" + fill="currentColor" + id="g7" + transform="matrix(1.2666667,0,0,1.25,-2.3000001,-2.75)"> + <path + d="M 3,19 H 4 V 3 H 3 Z m 6.293,-8 8,8 L 18,18.293 10.707,11 18,3.707 17.293,3 Z" + id="path3" /> + <path + d="m 4.293,11 8,8 L 13,18.293 5.707,11 13,3.707 12.293,3 Z" + id="path5" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/gotoLast.svg Fri Jan 13 18:20:54 2023 +0100 @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg9" + sodipodi:docname="gotoLast.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs13" /> + <sodipodi:namedview + id="namedview11" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg9" /> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + <g + class="ColorScheme-Text" + fill="currentColor" + id="g7" + transform="matrix(1.2666667,0,0,1.25,-3.5666667,-2.75)"> + <path + d="m 19,3 h -1 v 16 h 1 z m -6.293,8 -8,-8 L 4,3.707 11.293,11 4,18.293 4.707,19 Z" + id="path3" /> + <path + d="M 17.707,11 9.707,3 9,3.707 16.293,11 9,18.293 9.707,19 Z" + id="path5" /> + </g> +</svg>