Wed, 05 Jan 2022 14:26:11 +0100
Help Viewer
- added import and export capability to the bookmarks widget
--- a/eric7.epj Wed Jan 05 11:53:28 2022 +0100 +++ b/eric7.epj Wed Jan 05 14:26:11 2022 +0100 @@ -716,7 +716,8 @@ "eric7/JediInterface/RefactoringPreviewDialog.ui", "eric7/QScintilla/EditorOutlineSizesDialog.ui", "eric7/Preferences/ConfigurationPages/InterfaceLightPage.ui", - "eric7/HelpViewer/HelpBookmarkPropertiesDialog.ui" + "eric7/HelpViewer/HelpBookmarkPropertiesDialog.ui", + "eric7/HelpViewer/HelpBookmarksImportDialog.ui" ], "HASH": "df7daa8781250f7664e6ecaeaf1361fa2efd39ee", "IDLPARAMS": { @@ -2308,7 +2309,8 @@ "eric7/Preferences/ThemeManager.py", "eric7/Preferences/ConfigurationPages/InterfaceLightPage.py", "eric7/HelpViewer/HelpBookmarksWidget.py", - "eric7/HelpViewer/HelpBookmarkPropertiesDialog.py" + "eric7/HelpViewer/HelpBookmarkPropertiesDialog.py", + "eric7/HelpViewer/HelpBookmarksImportDialog.py" ], "SPELLEXCLUDES": "Dictionaries/excludes.dic", "SPELLLANGUAGE": "en_US",
--- a/eric7/HelpViewer/HelpBookmarkPropertiesDialog.py Wed Jan 05 11:53:28 2022 +0100 +++ b/eric7/HelpViewer/HelpBookmarkPropertiesDialog.py Wed Jan 05 14:26:11 2022 +0100 @@ -52,7 +52,7 @@ def getData(self): """ - Public method to retrieve the entered data + Public method to retrieve the entered data. @return tuple containing the title and URL for the bookmark @rtype tuple of (str, str)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/HelpViewer/HelpBookmarksImportDialog.py Wed Jan 05 14:26:11 2022 +0100 @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a dialog to enter the bookmarks import parameters. +""" + +from PyQt6.QtCore import pyqtSlot +from PyQt6.QtWidgets import QDialog, QDialogButtonBox + +from EricWidgets.EricPathPicker import EricPathPickerModes + +from .Ui_HelpBookmarksImportDialog import Ui_HelpBookmarksImportDialog + + +class HelpBookmarksImportDialog(QDialog, Ui_HelpBookmarksImportDialog): + """ + Class implementing a dialog to enter the bookmarks import parameters. + """ + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + self.setupUi(self) + + self.bookmarksPicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) + self.bookmarksPicker.setFilters( + self.tr("eric Bookmarks Files (*.json);;All Files (*)")) + self.bookmarksPicker.textChanged.connect(self.__updateOkButton) + + msh = self.minimumSizeHint() + self.resize(max(self.width(), msh.width()), msh.height()) + + self.__updateOkButton() + + @pyqtSlot() + def __updateOkButton(self): + """ + Private method to update the state of the OK button. + """ + self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled( + bool(self.bookmarksPicker.text())) + + def getData(self): + """ + Public method to retrieve the entered data. + + @return tuple containing a flag indicating to replace the existing + bookmarks and the path of the bookmarks file to be imported + @rtype tuple of (bool, str) + """ + return ( + self.replaceCheckBox.isChecked(), + self.bookmarksPicker.path(), + )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/HelpViewer/HelpBookmarksImportDialog.ui Wed Jan 05 14:26:11 2022 +0100 @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>HelpBookmarksImportDialog</class> + <widget class="QDialog" name="HelpBookmarksImportDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>500</width> + <height>98</height> + </rect> + </property> + <property name="windowTitle"> + <string>Import Bookmarks</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0" colspan="2"> + <widget class="QCheckBox" name="replaceCheckBox"> + <property name="toolTip"> + <string>Select to replace the existing bookmarks</string> + </property> + <property name="text"> + <string>Replace Existing Bookmarks</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Bookmarks:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="EricPathPicker" name="bookmarksPicker" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="focusPolicy"> + <enum>Qt::WheelFocus</enum> + </property> + <property name="toolTip"> + <string>Enter the path of the bookmarks file</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>EricPathPicker</class> + <extends>QWidget</extends> + <header>EricWidgets/EricPathPicker.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>HelpBookmarksImportDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>HelpBookmarksImportDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
--- a/eric7/HelpViewer/HelpBookmarksWidget.py Wed Jan 05 11:53:28 2022 +0100 +++ b/eric7/HelpViewer/HelpBookmarksWidget.py Wed Jan 05 14:26:11 2022 +0100 @@ -3,19 +3,29 @@ # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de> # +""" +Module implementing a widget showing the list of bookmarks. +""" + import contextlib +import datetime import json +import os from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QPoint, QUrl from PyQt6.QtGui import QClipboard, QGuiApplication from PyQt6.QtWidgets import ( - QAbstractItemView, QApplication, QDialog, QListWidget, QListWidgetItem, QMenu + QAbstractItemView, QApplication, QDialog, QListWidget, QListWidgetItem, + QMenu ) +from EricWidgets import EricFileDialog, EricMessageBox + import Preferences from .HelpBookmarkPropertiesDialog import HelpBookmarkPropertiesDialog + class HelpBookmarksWidget(QListWidget): """ Class implementing a widget showing the list of bookmarks. @@ -94,7 +104,10 @@ newBookmark = menu.addAction(self.tr("New Bookmark")) addBookmark = menu.addAction(self.tr("Bookmark Page")) menu.addSeparator() - deleteBookmarks = menu.addAction(self.tr("Delete All Bookmark")) + deleteBookmarks = menu.addAction(self.tr("Delete All Bookmarks")) + menu.addSeparator() + exportBookmarks = menu.addAction(self.tr("Export All Bookmarks")) + importBookmarks = menu.addAction(self.tr("Import Bookmarks")) act = menu.exec(self.mapToGlobal(point)) if act == openBookmarks: @@ -107,6 +120,10 @@ self.__deleteBookmarks([ self.item(row) for row in range(self.count()) ]) + elif act == exportBookmarks: + self.__exportBookmarks(selected=False) + elif act == importBookmarks: + self.__importBookmarks() @pyqtSlot(QPoint) def __showBookmarkContextMenu(self, point): @@ -140,6 +157,9 @@ editBookmark = menu.addAction(self.tr("Edit Bookmark")) menu.addSeparator() deleteBookmark = menu.addAction(self.tr("Delete Bookmark")) + menu.addSeparator() + exportBookmarks = menu.addAction(self.tr("Export All Bookmarks")) + importBookmarks = menu.addAction(self.tr("Import Bookmarks")) act = menu.exec(self.mapToGlobal(point)) if act == curPage: @@ -162,6 +182,10 @@ self.__editBookmark(itm) elif act == deleteBookmark: self.__deleteBookmarks([itm]) + elif act == exportBookmarks: + self.__exportBookmarks(selected=False) + elif act == importBookmarks: + self.__importBookmarks() @pyqtSlot(QPoint) def __showBookmarksContextMenu(self, point): @@ -175,12 +199,22 @@ openBookmarks = menu.addAction(self.tr("Open Selected Bookmarks")) menu.addSeparator() deleteBookmarks = menu.addAction(self.tr("Delete Selected Bookmarks")) + menu.addSeparator() + exportBookmarks = menu.addAction(self.tr("Export Selected Bookmarks")) + exportAllBookmarks = menu.addAction(self.tr("Export All Bookmarks")) + importBookmarks = menu.addAction(self.tr("Import Bookmarks")) act = menu.exec(self.mapToGlobal(point)) if act == openBookmarks: self.__openBookmarks(selected=True) elif act == deleteBookmarks: self.__deleteBookmarks(self.selectedItems()) + elif act == exportBookmarks: + self.__exportBookmarks(selected=True) + elif act == exportAllBookmarks: + self.__exportBookmarks(selected=False) + elif act == importBookmarks: + self.__importBookmarks() @pyqtSlot(str, str) def __addBookmark(self, title, url): @@ -358,17 +392,143 @@ }) Preferences.setHelp("Bookmarks", json.dumps(bookmarks)) - def __exportBookmarks(self): + @pyqtSlot() + def __exportBookmarks(self, selected=False): + """ + Private slot to export the bookmarks into a JSON file. + + @param selected flag indicating to export the selected bookmarks + (defaults to False) + @type bool (optional) """ - Private method to export the bookmarks into a JSON file. - """ - # TODO: not yet implemented + filename, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( + self, + self.tr("Export Bookmarks"), + "", + self.tr("eric Bookmarks Files (*.json);;All Files (*)"), + None, + EricFileDialog.DontConfirmOverwrite + ) + if filename: + ext = os.path.splitext(filename)[1] + if not ext: + ex = selectedFilter.split("(*")[1].split(")")[0] + if ex: + filename += ex + + if os.path.exists(filename): + ok = EricMessageBox.yesNo( + self, + self.tr("Export Bookmarks"), + self.tr("""The file <b>{0}</b> already exists. Do you""" + """ want to overwrite it?""").format(filename)) + if not ok: + return + + bookmarksDict = { + "creator": "eric7", + "version": 1, + "created": datetime.datetime.now().isoformat( + sep=" ", timespec="seconds"), + "bookmarks": [] + } + bookmarkItems = ( + self.selectedItems() + if selected else + [self.item(row) for row in range(self.count())] + ) + for bookmarkItem in bookmarkItems: + bookmarksDict["bookmarks"].append({ + "type": "url", + "title": bookmarkItem.text(), + "url": bookmarkItem.data(self.UrlRole).toString(), + }) + + jsonStr = json.dumps(bookmarksDict, indent=2, sort_keys=True) + try: + with open(filename, "w") as f: + f.write(jsonStr) + except OSError as err: + EricMessageBox.critical( + self, + self.tr("Export Bookmarks"), + self.tr("""<p>The bookmarks could not be exported""" + """ to <b>{0}</b>.</p><p>Reason: {1}</p>""") + .format(filename, str(err))) + @pyqtSlot() def __importBookmarks(self): """ - Private method to import bookmarks from a JSON file. + Private slot to import bookmarks from a JSON file. """ - # TODO: not yet implemented - # 1. read file - # 2. check, if exported from eric and compatible format version - # 3. process each entry + from .HelpBookmarksImportDialog import HelpBookmarksImportDialog + + dlg = HelpBookmarksImportDialog(self) + if dlg.exec() == QDialog.DialogCode.Accepted: + replace, filename = dlg.getData() + + try: + with open(filename, "r") as f: + jsonStr = f.read() + bookmarks = json.loads(jsonStr) + except (OSError, json.JSONDecodeError) as err: + EricMessageBox.critical( + self, + self.tr("Import Bookmarks"), + self.tr( + "<p>The bookmarks file <b>{0}</b> could not be " + "read.</p><p>Reason: {1}</p>" + ).format(filename, str(err)) + ) + return + + if not isinstance(bookmarks, dict): + EricMessageBox.critical( + self, + self.tr("Import Bookmarks"), + self.tr( + "The bookmarks file <b>{0}</b> has invalid contents." + ).format(filename) + ) + return + + try: + if bookmarks["creator"] != "eric7": + EricMessageBox.critical( + self, + self.tr("Import Bookmarks"), + self.tr( + "The bookmarks file <b>{0}</b> was not created" + " with 'eric7'." + ).format(filename) + ) + return + + if bookmarks["version"] != 1: + EricMessageBox.critical( + self, + self.tr("Import Bookmarks"), + self.tr( + "The bookmarks file <b>{0}</b> has an unsupported" + " format version." + ).format(filename) + ) + return + + if replace: + self.clear() + + for bookmark in bookmarks["bookmarks"]: + if bookmark["type"] == "url": + self.__addBookmark(bookmark["title"], bookmark["url"]) + self.sortItems() + self.__saveBookmarks() + + except KeyError: + EricMessageBox.critical( + self, + self.tr("Import Bookmarks"), + self.tr( + "The bookmarks file <b>{0}</b> has invalid contents." + ).format(filename) + )
--- a/eric7/HelpViewer/HelpViewerWidget.py Wed Jan 05 11:53:28 2022 +0100 +++ b/eric7/HelpViewer/HelpViewerWidget.py Wed Jan 05 14:26:11 2022 +0100 @@ -262,7 +262,6 @@ "documentFind", self.tr("Show the help search window")) self.__bookmarksButton = self.__addNavigationButton( "bookmark22", self.tr("Show list of bookmarks")) -# self.__openPagesButton.setChecked(True) self.__buttonLayout.addStretch()