eric7/HelpViewer/HelpViewerWidget.py

branch
eric7
changeset 8680
85503ff2fce9
parent 8679
fd172973428e
child 8681
6285e8374d99
equal deleted inserted replaced
8679:fd172973428e 8680:85503ff2fce9
5 5
6 """ 6 """
7 Module implementing an embedded viewer for QtHelp and local HTML files. 7 Module implementing an embedded viewer for QtHelp and local HTML files.
8 """ 8 """
9 9
10 from PyQt6.QtCore import pyqtSlot, QUrl 10 import os
11 from PyQt6.QtGui import QTextDocument 11
12 from PyQt6.QtCore import pyqtSlot, Qt, QUrl
13 from PyQt6.QtGui import QAction
14 from PyQt6.QtHelp import QHelpEngine
12 from PyQt6.QtWidgets import ( 15 from PyQt6.QtWidgets import (
13 QWidget, QHBoxLayout, QVBoxLayout, QComboBox, QSizePolicy, QStackedWidget, 16 QWidget, QHBoxLayout, QVBoxLayout, QComboBox, QSizePolicy, QStackedWidget,
14 QToolButton, QButtonGroup, QAbstractButton 17 QToolButton, QButtonGroup, QAbstractButton, QMenu
15 ) 18 )
16 19
20 from EricWidgets import EricFileDialog, EricMessageBox
21
17 import UI.PixmapCache 22 import UI.PixmapCache
23 import Utilities
24 import Preferences
18 25
19 from .OpenPagesWidget import OpenPagesWidget 26 from .OpenPagesWidget import OpenPagesWidget
20 27
21 28
22 class HelpViewerWidget(QWidget): 29 class HelpViewerWidget(QWidget):
31 @type QWidget (optional) 38 @type QWidget (optional)
32 """ 39 """
33 super().__init__(parent) 40 super().__init__(parent)
34 self.setObjectName("HelpViewerWidget") 41 self.setObjectName("HelpViewerWidget")
35 42
43 self.__ui = parent
44
36 self.__layout = QVBoxLayout() 45 self.__layout = QVBoxLayout()
37 self.__layout.setObjectName("MainLayout") 46 self.__layout.setObjectName("MainLayout")
38 self.__layout.setContentsMargins(0, 3, 0, 0) 47 self.__layout.setContentsMargins(0, 3, 0, 0)
39 48
40 ################################################################### 49 ###################################################################
44 self.__helpSelector = QComboBox(self) 53 self.__helpSelector = QComboBox(self)
45 self.__helpSelector.setSizePolicy( 54 self.__helpSelector.setSizePolicy(
46 QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) 55 QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
47 self.__selectorLayout.addWidget(self.__helpSelector) 56 self.__selectorLayout.addWidget(self.__helpSelector)
48 self.__populateHelpSelector() 57 self.__populateHelpSelector()
58 self.__helpSelector.currentIndexChanged.connect(
59 self.__helpTopicSelected)
49 60
50 self.__openButton = QToolButton(self) 61 self.__openButton = QToolButton(self)
51 self.__openButton.setIcon(UI.PixmapCache.getIcon("open")) 62 self.__openButton.setIcon(UI.PixmapCache.getIcon("open"))
52 self.__openButton.setToolTip(self.tr("Open a local file")) 63 self.__openButton.setToolTip(self.tr("Open a local file"))
53 self.__openButton.clicked.connect(self.__openFile) 64 self.__openButton.clicked.connect(self.__openFile)
54 self.__selectorLayout.addWidget(self.__openButton) 65 self.__selectorLayout.addWidget(self.__openButton)
55 66
67 self.__actionsButton = QToolButton(self)
68 self.__actionsButton.setIcon(
69 UI.PixmapCache.getIcon("actionsToolButton"))
70 self.__actionsButton.setToolTip(
71 self.tr("Select action from menu"))
72 self.__actionsButton.setPopupMode(
73 QToolButton.ToolButtonPopupMode.InstantPopup)
74 self.__selectorLayout.addWidget(self.__actionsButton)
75
56 self.__layout.addLayout(self.__selectorLayout) 76 self.__layout.addLayout(self.__selectorLayout)
57 77
78 ###################################################################
79
80 self.__navButtonsLayout = QHBoxLayout()
81
82 self.__navButtonsLayout.addStretch()
83
84 # TODO: add backward button
85 self.__backwardButton = QToolButton(self)
86 self.__backwardButton.setIcon(UI.PixmapCache.getIcon("back"))
87 self.__backwardButton.setToolTip(self.tr("Move one page backward"))
88 self.__backwardButton.setToolButtonStyle(
89 Qt.ToolButtonStyle.ToolButtonIconOnly)
90 self.__backwardButton.setAutoRaise(True)
91 self.__backwardButton.clicked.connect(self.__backward)
92
93 # TODO: add forward button
94 self.__forwardButton = QToolButton(self)
95 self.__forwardButton.setIcon(UI.PixmapCache.getIcon("forward"))
96 self.__forwardButton.setToolTip(self.tr("Move one page forward"))
97 self.__forwardButton.setToolButtonStyle(
98 Qt.ToolButtonStyle.ToolButtonIconOnly)
99 self.__forwardButton.setAutoRaise(True)
100 self.__forwardButton.clicked.connect(self.__forward)
101
102 self.__backForButtonLayout = QHBoxLayout()
103 self.__backForButtonLayout.setContentsMargins(0, 0, 0, 0)
104 self.__backForButtonLayout.setSpacing(0)
105 self.__backForButtonLayout.addWidget(self.__backwardButton)
106 self.__backForButtonLayout.addWidget(self.__forwardButton)
107 self.__navButtonsLayout.addLayout(self.__backForButtonLayout)
108
109 # TODO: add reload button
110 self.__reloadButton = QToolButton(self)
111 self.__reloadButton.setIcon(UI.PixmapCache.getIcon("reload"))
112 self.__reloadButton.setToolTip(self.tr("Reload the current page"))
113 self.__reloadButton.clicked.connect(self.__reload)
114 self.__navButtonsLayout.addWidget(self.__reloadButton)
115
116 # TODO: add zoom in button
117 # TODO: add zoom out button
118 # TODO: add zoom reset button
119
120 self.__navButtonsLayout.addStretch()
121
122 self.__layout.addLayout(self.__navButtonsLayout)
123
124 self.__backMenu = QMenu(self)
125 self.__backMenu.triggered.connect(self.__navigationMenuActionTriggered)
126 self.__backwardButton.setMenu(self.__backMenu)
127 self.__backMenu.aboutToShow.connect(self.__showBackMenu)
128
129 self.__forwardMenu = QMenu(self)
130 self.__forwardMenu.triggered.connect(
131 self.__navigationMenuActionTriggered)
132 self.__forwardButton.setMenu(self.__forwardMenu)
133 self.__forwardMenu.aboutToShow.connect(self.__showForwardMenu)
134
58 ################################################################### 135 ###################################################################
59 136
60 self.__helpStack = QStackedWidget(self) 137 self.__helpStack = QStackedWidget(self)
61 self.__helpStack.setSizePolicy( 138 self.__helpStack.setSizePolicy(
62 QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) 139 QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
80 self.__buttonGroup.buttonClicked.connect( 157 self.__buttonGroup.buttonClicked.connect(
81 self.__selectNavigationWidget) 158 self.__selectNavigationWidget)
82 159
83 self.__buttonLayout.addStretch() 160 self.__buttonLayout.addStretch()
84 161
85 self.__openPagesButton = QToolButton(self) 162 self.__openPagesButton = self.__addNavigationButton(
86 self.__openPagesButton.setIcon(UI.PixmapCache.getIcon("fileMisc")) 163 "fileMisc", self.tr("Show list of open pages"))
87 self.__openPagesButton.setToolTip(self.tr("Show list of open pages")) 164
88 self.__openPagesButton.setCheckable(True) 165 # TODO: add buttons for the QHelp related widgets
89 self.__buttonGroup.addButton(self.__openPagesButton)
90 self.__buttonLayout.addWidget(self.__openPagesButton)
91 166
92 self.__buttonLayout.addStretch() 167 self.__buttonLayout.addStretch()
93 168
94 self.__layout.addLayout(self.__buttonLayout) 169 self.__layout.addLayout(self.__buttonLayout)
95 170
97 172
98 self.setLayout(self.__layout) 173 self.setLayout(self.__layout)
99 174
100 self.__openPagesButton.setChecked(True) 175 self.__openPagesButton.setChecked(True)
101 176
177 self.__initHelpEngine()
178
179 self.__ui.preferencesChanged.connect(self.__populateHelpSelector)
180
181 self.__initActionsMenu()
182
102 self.addPage() 183 self.addPage()
184 self.__checkActionButtons(0)
185
186 def __addNavigationButton(self, iconName, toolTip):
187 """
188 Private method to create and add a navigation button.
189
190 @param iconName name of the icon
191 @type str
192 @param toolTip tooltip to be shown
193 @type str
194 @return reference to the created button
195 @rtype QToolButton
196 """
197 button = QToolButton(self)
198 button.setIcon(UI.PixmapCache.getIcon(iconName))
199 button.setToolTip(toolTip)
200 button.setCheckable(True)
201 self.__buttonGroup.addButton(button)
202 self.__buttonLayout.addWidget(button)
203
204 return button
103 205
104 def __populateNavigationStack(self): 206 def __populateNavigationStack(self):
105 """ 207 """
106 Private method to populate the stack of navigation widgets. 208 Private method to populate the stack of navigation widgets.
107 """ 209 """
108 self.__openPagesList = OpenPagesWidget(self.__helpStack, self) 210 self.__openPagesList = OpenPagesWidget(self.__helpStack, self)
211 self.__openPagesList.currentChanged.connect(self.__checkActionButtons)
109 self.__helpNavigationStack.addWidget(self.__openPagesList) 212 self.__helpNavigationStack.addWidget(self.__openPagesList)
110 213
111 # TODO: not yet implemented 214 # TODO: not yet implemented
112 215
113 @pyqtSlot(QAbstractButton) 216 @pyqtSlot(QAbstractButton)
125 228
126 def __populateHelpSelector(self): 229 def __populateHelpSelector(self):
127 """ 230 """
128 Private method to populate the help selection combo box. 231 Private method to populate the help selection combo box.
129 """ 232 """
130 # TODO: not yet implemented 233 self.__helpSelector.clear()
234
235 self.__helpSelector.addItem("", "")
236
237 for key, topic in [
238 ("EricDocDir", self.tr("eric API Documentation")),
239 ("PythonDocDir", self.tr("Python 3 Documentation")),
240 ("Qt5DocDir", self.tr("Qt5 Documentation")),
241 ("Qt6DocDir", self.tr("Qt6 Documentation")),
242 ("PyQt5DocDir", self.tr("PyQt5 Documentation")),
243 ("PyQt6DocDir", self.tr("PyQt6 Documentation")),
244 ("PySide2DocDir", self.tr("PySide2 Documentation")),
245 ("PySide6DocDir", self.tr("PySide6 Documentation")),
246 ]:
247 urlStr = Preferences.getHelp(key)
248 if urlStr:
249 self.__helpSelector.addItem(topic, urlStr)
250
251 @pyqtSlot()
252 def __helpTopicSelected(self):
253 """
254 Private slot handling the selection of a new help topic.
255 """
256 urlStr = self.__helpSelector.currentData()
257 if urlStr:
258 url = QUrl(urlStr)
259 self.currentViewer().setUrl(url)
131 260
132 def searchQtHelp(self, searchExpression): 261 def searchQtHelp(self, searchExpression):
133 """ 262 """
134 Public method to search for a given search expression. 263 Public method to search for a given search expression.
135 264
153 @pyqtSlot() 282 @pyqtSlot()
154 def __openFile(self): 283 def __openFile(self):
155 """ 284 """
156 Private slot to open a local help file (*.html). 285 Private slot to open a local help file (*.html).
157 """ 286 """
158 # TODO: not yet implemented 287 htmlFile = EricFileDialog.getOpenFileName(
288 self,
289 self.tr("Open HTML File"),
290 "",
291 self.tr("HTML Files (*.htm *.html);;All Files (*)")
292 )
293 if htmlFile:
294 self.currentViewer().setUrl(QUrl.fromLocalFile(htmlFile))
159 295
160 def addPage(self, url=QUrl("about:blank")): 296 def addPage(self, url=QUrl("about:blank")):
161 """ 297 """
162 Public method to add a new help page with the given URL. 298 Public method to add a new help page with the given URL.
163 299
164 @param url requested URL (defaults to QUrl("about:blank")) 300 @param url requested URL (defaults to QUrl("about:blank"))
165 @type QUrl (optional) 301 @type QUrl (optional)
166 """ 302 """
303 viewer = self.__newViewer()
304 viewer.setUrl(url)
305
306 self.__helpStack.addWidget(viewer)
307 self.__openPagesList.addPage(viewer)
308
309 def __newViewer(self):
310 """
311 Private method to create a new help viewer.
312
313 @return help viewer
314 @rtype HelpViewerImpl
315 """
167 try: 316 try:
168 from .HelpViewerImpl_qwe import HelpViewerImpl_qwe 317 from .HelpViewerImpl_qwe import HelpViewerImpl_qwe
169 viewer = HelpViewerImpl_qwe(self) 318 viewer = HelpViewerImpl_qwe(self.__helpEngine, self)
170 except ImportError: 319 except ImportError:
171 from .HelpViewerImpl_qtb import HelpViewerImpl_qtb 320 from .HelpViewerImpl_qtb import HelpViewerImpl_qtb
172 viewer = HelpViewerImpl_qtb(self) 321 viewer = HelpViewerImpl_qtb(self.__helpEngine, self)
173 322 return viewer
174 self.__helpStack.addWidget(viewer) 323
175 self.__openPagesList.addPage(viewer) 324 def currentViewer(self):
176 325 """
177 viewer.setSource(url, QTextDocument.ResourceType.HtmlResource) 326 Public method to get the active viewer.
327
328 @return reference to the active help viewer
329 @rtype HelpViewerImpl
330 """
331 return self.__helpStack.currentWidget()
332
333 #######################################################################
334 ## QtHelp related code below
335 #######################################################################
336
337 def __initHelpEngine(self):
338 """
339 Private method to initialize the QtHelp related stuff.
340 """
341 self.__helpEngine = QHelpEngine(
342 self.__getQtHelpCollectionFileName(),
343 self)
344 self.__helpEngine.setReadOnly(False)
345 self.__helpEngine.setupData()
346 self.__helpEngine.setUsesFilterEngine(True)
347 self.__removeOldDocumentation()
348 self.__helpEngine.warning.connect(self.__warning)
349
350 def __getQtHelpCollectionFileName(cls):
351 """
352 Private method to determine the name of the QtHelp collection file.
353
354 @return path of the QtHelp collection file
355 @rtype str
356 """
357 qthelpDir = os.path.join(Utilities.getConfigDir(), "qthelp")
358 if not os.path.exists(qthelpDir):
359 os.makedirs(qthelpDir)
360 return os.path.join(qthelpDir, "eric7help.qhc")
361
362 @pyqtSlot(str)
363 def __warning(self, msg):
364 """
365 Private slot handling warnings of the help engine.
366
367 @param msg message sent by the help engine
368 @type str
369 """
370 EricMessageBox.warning(
371 self,
372 self.tr("Help Engine"), msg)
373
374 @pyqtSlot()
375 def __removeOldDocumentation(self):
376 """
377 Private slot to remove non-existing documentation from the help engine.
378 """
379 for namespace in self.__helpEngine.registeredDocumentations():
380 docFile = self.__helpEngine.documentationFileName(namespace)
381 if not os.path.exists(docFile):
382 self.__helpEngine.unregisterDocumentation(namespace)
383
384 @pyqtSlot()
385 def __manageQtHelpDocuments(self):
386 """
387 Private slot to manage the QtHelp documentation database.
388 """
389 from WebBrowser.QtHelp.QtHelpDocumentationConfigurationDialog import (
390 QtHelpDocumentationConfigurationDialog
391 )
392 dlg = QtHelpDocumentationConfigurationDialog(
393 self.__helpEngine, self)
394 dlg.exec()
395
396 @pyqtSlot()
397 def __reindexDocumentation(self):
398 """
399 Private slot
400 """
401
402 #######################################################################
403 ## Actions Menu related methods
404 #######################################################################
405
406 def __initActionsMenu(self):
407 """
408 Private method to initialize the actions menu.
409 """
410 self.__actionsMenu = QMenu()
411 self.__actionsMenu.setToolTipsVisible(True)
412
413 self.__actionsMenu.addAction(self.tr("Manage QtHelp Documents"),
414 self.__manageQtHelpDocuments)
415 act = self.__actionsMenu.addAction(self.tr("Reindex Documentation"),
416 self.__reindexDocumentation)
417 ## act.triggered.connect(self.__searchEngine.reindexDocumentation)
418
419 self.__actionsButton.setMenu(self.__actionsMenu)
420
421 #######################################################################
422 ## Navigation related methods below
423 #######################################################################
424
425 @pyqtSlot()
426 def __backward(self):
427 """
428 Private slot to move one page backward.
429 """
430 cv = self.currentViewer()
431 if cv:
432 cv.backward()
433
434 @pyqtSlot()
435 def __forward(self):
436 """
437 Private slot to move one page foreward.
438 """
439 cv = self.currentViewer()
440 if cv:
441 cv.forward()
442
443 @pyqtSlot()
444 def __reload(self):
445 """
446 Private slot to reload the current page.
447 """
448 cv = self.currentViewer()
449 if cv:
450 cv.reload()
451
452 @pyqtSlot(int)
453 def __checkActionButtons(self, row):
454 """
455 Private slot to set the enabled state of the action buttons.
456
457 @param row index of the current page
458 @type int
459 """
460 cv = self.currentViewer()
461 self.__backwardButton.setEnabled(cv and cv.isBackwardAvailable())
462 self.__forwardButton.setEnabled(cv and cv.isForwardAvailable())
463
464 def __showBackMenu(self):
465 """
466 Private slot showing the backward navigation menu.
467 """
468 cv = self.currentViewer()
469 if cv:
470 self.__backMenu.clear()
471 backwardHistoryCount = max(cv.backwardHistoryCount(), 20)
472 # show max. 20 items
473
474 for index in range(1, backwardHistoryCount + 1):
475 act = QAction(self)
476 act.setData(-index)
477 act.setText(cv.historyTitle(-index))
478 self.__backMenu.addAction(act)
479
480 self.__backMenu.addSeparator()
481 self.__backMenu.addAction(self.tr("Clear History"),
482 self.__clearHistory)
483
484 def __showForwardMenu(self):
485 """
486 Private slot showing the forward navigation menu.
487 """
488 cv = self.currentViewer()
489 if cv:
490 self.__forwardMenu.clear()
491 forwardHistoryCount = max(cv.forwardHistoryCount(), 20)
492 # show max. 20 items
493
494 for index in range(1, forwardHistoryCount + 1):
495 act = QAction(self)
496 act.setData(index)
497 act.setText(cv.historyTitle(index))
498 self.__forwardMenu.addAction(act)
499
500 self.__forwardMenu.addSeparator()
501 self.__forwardMenu.addAction(self.tr("Clear History"),
502 self.__clearHistory)
503
504 def __navigationMenuActionTriggered(self, act):
505 """
506 Private slot to go to the selected page.
507
508 @param act reference to the action selected in the navigation menu
509 @type QAction
510 """
511 cv = self.currentViewer()
512 if cv:
513 index = act.data()
514 cv.gotoHistory(index)
515
516 def __clearHistory(self):
517 """
518 Private slot to clear the history of the current viewer.
519 """
520 cb = self.__mw.currentBrowser()
521 if cb is not None:
522 cb.history().clear()
523 self.__mw.setForwardAvailable(cb.isForwardAvailable())
524 self.__mw.setBackwardAvailable(cb.isBackwardAvailable())

eric ide

mercurial