eric6/WebBrowser/WebBrowserTabWidget.py

changeset 6942
2602857055c5
parent 6656
273a2130adbe
child 6999
f23e546a7eb9
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2010 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the central widget showing the web pages.
8 """
9
10 from __future__ import unicode_literals
11
12 import os
13
14 from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl, QDir, QFile, \
15 QFileDevice, QTemporaryFile
16 from PyQt5.QtGui import QIcon, QPixmap, QPainter
17 from PyQt5.QtWidgets import QWidget, QHBoxLayout, QMenu, QToolButton, \
18 QDialog, QApplication
19 from PyQt5.QtPrintSupport import QPrinter, QPrintDialog, QAbstractPrintDialog
20
21 from E5Gui.E5TabWidget import E5TabWidget
22 from E5Gui import E5MessageBox
23 from E5Gui.E5Application import e5App
24
25 from .WebBrowserView import WebBrowserView
26 from .WebBrowserPage import WebBrowserPage
27 from .Tools import WebBrowserTools
28 from .Tools import FilePrinter
29 from . import WebInspector
30
31 import UI.PixmapCache
32
33 import Utilities
34 import Preferences
35 import Globals
36 from Globals import qVersionTuple
37
38 from eric6config import getConfig
39
40
41 class WebBrowserTabWidget(E5TabWidget):
42 """
43 Class implementing the central widget showing the web pages.
44
45 @signal sourceChanged(WebBrowserView, QUrl) emitted after the URL of a
46 browser has changed
47 @signal currentUrlChanged(QUrl) emitted after the URL of the current
48 browser has changed
49 @signal titleChanged(WebBrowserView, str) emitted after the title of a
50 browser has changed
51 @signal showMessage(str) emitted to show a message in the main window
52 status bar
53 @signal browserOpened(QWidget) emitted after a new browser was created
54 @signal browserClosed(QWidget) emitted after a browser was closed
55 @signal browserZoomValueChanged(int) emitted to signal a change of the
56 current browser's zoom level
57 """
58 sourceChanged = pyqtSignal(WebBrowserView, QUrl)
59 currentUrlChanged = pyqtSignal(QUrl)
60 titleChanged = pyqtSignal(WebBrowserView, str)
61 showMessage = pyqtSignal(str)
62 browserOpened = pyqtSignal(QWidget)
63 browserClosed = pyqtSignal(QWidget)
64 browserZoomValueChanged = pyqtSignal(int)
65
66 def __init__(self, parent):
67 """
68 Constructor
69
70 @param parent reference to the parent widget (QWidget)
71 """
72 super(WebBrowserTabWidget, self).__init__(parent, dnd=True)
73
74 from .WebBrowserTabBar import WebBrowserTabBar
75 self.__tabBar = WebBrowserTabBar(self)
76 self.setCustomTabBar(True, self.__tabBar)
77
78 self.__mainWindow = parent
79
80 self.setUsesScrollButtons(True)
81 self.setDocumentMode(True)
82 self.setElideMode(Qt.ElideNone)
83
84 from .ClosedTabsManager import ClosedTabsManager
85 self.__closedTabsManager = ClosedTabsManager(self)
86 self.__closedTabsManager.closedTabAvailable.connect(
87 self.__closedTabAvailable)
88
89 from .UrlBar.StackedUrlBar import StackedUrlBar
90 self.__stackedUrlBar = StackedUrlBar(self)
91 self.__tabBar.tabMoved.connect(self.__stackedUrlBar.moveBar)
92
93 self.__tabContextMenuIndex = -1
94 self.currentChanged[int].connect(self.__currentChanged)
95 self.setTabContextMenuPolicy(Qt.CustomContextMenu)
96 self.customTabContextMenuRequested.connect(self.__showContextMenu)
97
98 self.__rightCornerWidget = QWidget(self)
99 self.__rightCornerWidgetLayout = QHBoxLayout(self.__rightCornerWidget)
100 self.__rightCornerWidgetLayout.setContentsMargins(0, 0, 0, 0)
101 self.__rightCornerWidgetLayout.setSpacing(0)
102
103 self.__navigationMenu = QMenu(self)
104 self.__navigationMenu.aboutToShow.connect(self.__showNavigationMenu)
105 self.__navigationMenu.triggered.connect(self.__navigationMenuTriggered)
106
107 self.__navigationButton = QToolButton(self)
108 self.__navigationButton.setIcon(
109 UI.PixmapCache.getIcon("1downarrow.png"))
110 self.__navigationButton.setToolTip(
111 self.tr("Show a navigation menu"))
112 self.__navigationButton.setPopupMode(QToolButton.InstantPopup)
113 self.__navigationButton.setMenu(self.__navigationMenu)
114 self.__navigationButton.setEnabled(False)
115 self.__rightCornerWidgetLayout.addWidget(self.__navigationButton)
116
117 self.__closedTabsMenu = QMenu(self)
118 self.__closedTabsMenu.aboutToShow.connect(
119 self.__aboutToShowClosedTabsMenu)
120
121 self.__closedTabsButton = QToolButton(self)
122 self.__closedTabsButton.setIcon(UI.PixmapCache.getIcon("trash.png"))
123 self.__closedTabsButton.setToolTip(
124 self.tr("Show a navigation menu for closed tabs"))
125 self.__closedTabsButton.setPopupMode(QToolButton.InstantPopup)
126 self.__closedTabsButton.setMenu(self.__closedTabsMenu)
127 self.__closedTabsButton.setEnabled(False)
128 self.__rightCornerWidgetLayout.addWidget(self.__closedTabsButton)
129
130 self.__closeButton = QToolButton(self)
131 self.__closeButton.setIcon(UI.PixmapCache.getIcon("close.png"))
132 self.__closeButton.setToolTip(
133 self.tr("Close the current web browser"))
134 self.__closeButton.setEnabled(False)
135 self.__closeButton.clicked.connect(self.closeBrowser)
136 self.__rightCornerWidgetLayout.addWidget(self.__closeButton)
137 if Preferences.getUI("SingleCloseButton") or \
138 not hasattr(self, 'setTabsClosable'):
139 self.__closeButton.show()
140 else:
141 self.setTabsClosable(True)
142 self.tabCloseRequested.connect(self.closeBrowserAt)
143 self.__closeButton.hide()
144
145 self.setCornerWidget(self.__rightCornerWidget, Qt.TopRightCorner)
146
147 self.__newTabButton = QToolButton(self)
148 self.__newTabButton.setIcon(UI.PixmapCache.getIcon("plus.png"))
149 self.__newTabButton.setToolTip(
150 self.tr("Open a new web browser tab"))
151 self.setCornerWidget(self.__newTabButton, Qt.TopLeftCorner)
152 self.__newTabButton.clicked.connect(self.__newBrowser)
153
154 self.__initTabContextMenu()
155
156 self.__historyCompleter = None
157
158 self.__pdfPrinter = None
159
160 def __initTabContextMenu(self):
161 """
162 Private method to create the tab context menu.
163 """
164 self.__tabContextMenu = QMenu(self)
165 self.tabContextNewAct = self.__tabContextMenu.addAction(
166 UI.PixmapCache.getIcon("tabNew.png"),
167 self.tr('New Tab'), self.newBrowser)
168 self.__tabContextMenu.addSeparator()
169 self.leftMenuAct = self.__tabContextMenu.addAction(
170 UI.PixmapCache.getIcon("1leftarrow.png"),
171 self.tr('Move Left'), self.__tabContextMenuMoveLeft)
172 self.rightMenuAct = self.__tabContextMenu.addAction(
173 UI.PixmapCache.getIcon("1rightarrow.png"),
174 self.tr('Move Right'), self.__tabContextMenuMoveRight)
175 self.__tabContextMenu.addSeparator()
176 self.tabContextCloneAct = self.__tabContextMenu.addAction(
177 self.tr("Duplicate Page"), self.__tabContextMenuClone)
178 self.__tabContextMenu.addSeparator()
179 self.tabContextCloseAct = self.__tabContextMenu.addAction(
180 UI.PixmapCache.getIcon("tabClose.png"),
181 self.tr('Close'), self.__tabContextMenuClose)
182 self.tabContextCloseOthersAct = self.__tabContextMenu.addAction(
183 UI.PixmapCache.getIcon("tabCloseOther.png"),
184 self.tr("Close Others"), self.__tabContextMenuCloseOthers)
185 self.__tabContextMenu.addAction(
186 self.tr('Close All'), self.closeAllBrowsers)
187 self.__tabContextMenu.addSeparator()
188 if qVersionTuple() >= (5, 8, 0) or (
189 not Globals.isWindowsPlatform() and qVersionTuple() < (5, 7, 0)):
190 self.__tabContextMenu.addAction(
191 UI.PixmapCache.getIcon("printPreview.png"),
192 self.tr('Print Preview'), self.__tabContextMenuPrintPreview)
193 if qVersionTuple() >= (5, 8, 0) or (
194 not Globals.isWindowsPlatform() or qVersionTuple() >= (5, 7, 0)):
195 self.__tabContextMenu.addAction(
196 UI.PixmapCache.getIcon("print.png"),
197 self.tr('Print'), self.__tabContextMenuPrint)
198 if Globals.isLinuxPlatform() or qVersionTuple() >= (5, 7, 0):
199 self.__tabContextMenu.addAction(
200 UI.PixmapCache.getIcon("printPdf.png"),
201 self.tr('Print as PDF'), self.__tabContextMenuPrintPdf)
202 self.__tabContextMenu.addSeparator()
203 if hasattr(WebBrowserPage, "isAudioMuted"):
204 self.__audioAct = self.__tabContextMenu.addAction(
205 "", self.__tabContextMenuAudioMute)
206 self.__tabContextMenu.addSeparator()
207 else:
208 self.__audioAct = None
209 self.__tabContextMenu.addAction(
210 UI.PixmapCache.getIcon("reload.png"),
211 self.tr('Reload All'), self.reloadAllBrowsers)
212 self.__tabContextMenu.addSeparator()
213 self.__tabContextMenu.addAction(
214 UI.PixmapCache.getIcon("addBookmark.png"),
215 self.tr('Bookmark All Tabs'), self.__mainWindow.bookmarkAll)
216
217 self.__tabBackContextMenu = QMenu(self)
218 self.__tabBackContextMenu.addAction(
219 self.tr('Close All'), self.closeAllBrowsers)
220 self.__tabBackContextMenu.addAction(
221 UI.PixmapCache.getIcon("reload.png"),
222 self.tr('Reload All'), self.reloadAllBrowsers)
223 self.__tabBackContextMenu.addAction(
224 UI.PixmapCache.getIcon("addBookmark.png"),
225 self.tr('Bookmark All Tabs'), self.__mainWindow.bookmarkAll)
226 self.__tabBackContextMenu.addSeparator()
227 self.__restoreClosedTabAct = self.__tabBackContextMenu.addAction(
228 UI.PixmapCache.getIcon("trash.png"),
229 self.tr('Restore Closed Tab'))
230 self.__restoreClosedTabAct.setEnabled(False)
231 self.__restoreClosedTabAct.setData(0)
232 self.__restoreClosedTabAct.triggered.connect(
233 lambda: self.restoreClosedTab(self.__restoreClosedTabAct))
234
235 def __showContextMenu(self, coord, index):
236 """
237 Private slot to show the tab context menu.
238
239 @param coord the position of the mouse pointer (QPoint)
240 @param index index of the tab the menu is requested for (integer)
241 """
242 coord = self.mapToGlobal(coord)
243 if index == -1:
244 self.__tabBackContextMenu.popup(coord)
245 else:
246 self.__tabContextMenuIndex = index
247 self.leftMenuAct.setEnabled(index > 0)
248 self.rightMenuAct.setEnabled(index < self.count() - 1)
249
250 self.tabContextCloseOthersAct.setEnabled(self.count() > 1)
251
252 if self.__audioAct is not None:
253 if self.widget(self.__tabContextMenuIndex).page()\
254 .isAudioMuted():
255 self.__audioAct.setText(self.tr("Unmute Tab"))
256 self.__audioAct.setIcon(
257 UI.PixmapCache.getIcon("audioVolumeHigh.png"))
258 else:
259 self.__audioAct.setText(self.tr("Mute Tab"))
260 self.__audioAct.setIcon(
261 UI.PixmapCache.getIcon("audioVolumeMuted.png"))
262
263 self.__tabContextMenu.popup(coord)
264
265 def __tabContextMenuMoveLeft(self):
266 """
267 Private method to move a tab one position to the left.
268 """
269 self.moveTab(self.__tabContextMenuIndex,
270 self.__tabContextMenuIndex - 1)
271
272 def __tabContextMenuMoveRight(self):
273 """
274 Private method to move a tab one position to the right.
275 """
276 self.moveTab(self.__tabContextMenuIndex,
277 self.__tabContextMenuIndex + 1)
278
279 def __tabContextMenuClone(self):
280 """
281 Private method to clone the selected tab.
282 """
283 idx = self.__tabContextMenuIndex
284 if idx < 0:
285 idx = self.currentIndex()
286 if idx < 0 or idx > self.count():
287 return
288
289 url = self.widget(idx).url()
290 self.newBrowser(url)
291
292 def __tabContextMenuClose(self):
293 """
294 Private method to close the selected tab.
295 """
296 self.closeBrowserAt(self.__tabContextMenuIndex)
297
298 def __tabContextMenuCloseOthers(self):
299 """
300 Private slot to close all other tabs.
301 """
302 index = self.__tabContextMenuIndex
303 for i in list(range(self.count() - 1, index, -1)) + \
304 list(range(index - 1, -1, -1)):
305 self.closeBrowserAt(i)
306
307 def __tabContextMenuPrint(self):
308 """
309 Private method to print the selected tab.
310 """
311 browser = self.widget(self.__tabContextMenuIndex)
312 self.printBrowser(browser)
313
314 def __tabContextMenuPrintPdf(self):
315 """
316 Private method to print the selected tab as PDF.
317 """
318 browser = self.widget(self.__tabContextMenuIndex)
319 self.printBrowserPdf(browser)
320
321 def __tabContextMenuPrintPreview(self):
322 """
323 Private method to show a print preview of the selected tab.
324 """
325 browser = self.widget(self.__tabContextMenuIndex)
326 self.printPreviewBrowser(browser)
327
328 def __tabContextMenuAudioMute(self):
329 """
330 Private method to mute or unmute the selected tab.
331 """
332 page = self.widget(self.__tabContextMenuIndex).page()
333 muted = page.isAudioMuted()
334 page.setAudioMuted(not muted)
335
336 @pyqtSlot(bool)
337 def __recentlyAudibleChanged(self, recentlyAudible, page):
338 """
339 Private slot to react on the audible state of a page.
340
341 @param recentlyAudible flag indicating the new audible state
342 @type bool
343 @param page reference to the web page
344 @type WebBrowserPage
345 """
346 browser = page.view()
347 if browser is None:
348 return
349
350 index = self.indexOf(browser)
351 icon = page.icon()
352
353 if page.isAudioMuted() or (
354 not page.isAudioMuted() and recentlyAudible):
355 pix = QPixmap(32, 32)
356 pix.fill(Qt.transparent)
357 painter = QPainter(pix)
358 icon.paint(painter, 0, 0, 22, 22)
359 if page.isAudioMuted():
360 audioIcon = UI.PixmapCache.getIcon("audioMuted.png")
361 else:
362 audioIcon = UI.PixmapCache.getIcon("audioPlaying.png")
363 audioIcon.paint(painter, 13, 13, 18, 18)
364 painter.end()
365 self.setTabIcon(index, QIcon(pix))
366 else:
367 self.setTabIcon(index, icon)
368
369 @pyqtSlot()
370 def __newBrowser(self):
371 """
372 Private slot to open a new browser tab.
373 """
374 self.newBrowser()
375
376 def newBrowser(self, link=None, position=-1, background=False,
377 restoreSession=False):
378 """
379 Public method to create a new web browser tab.
380
381 @param link link to be shown
382 @type str or QUrl
383 @keyparam position position to create the new tab at or -1 to add it
384 to the end
385 @type int
386 @keyparam background flag indicating to open the tab in the
387 background
388 @type bool
389 @keyparam restoreSession flag indicating a restore session action
390 @type bool
391 @return reference to the new browser
392 @rtype WebBrowserView
393 """
394 if link is None:
395 linkName = ""
396 elif isinstance(link, QUrl):
397 linkName = link.toString()
398 else:
399 linkName = link
400
401 from .UrlBar.UrlBar import UrlBar
402 urlbar = UrlBar(self.__mainWindow, self)
403 if self.__historyCompleter is None:
404 import WebBrowser.WebBrowserWindow
405 from .History.HistoryCompleter import HistoryCompletionModel, \
406 HistoryCompleter
407 self.__historyCompletionModel = HistoryCompletionModel(self)
408 self.__historyCompletionModel.setSourceModel(
409 WebBrowser.WebBrowserWindow.WebBrowserWindow.historyManager()
410 .historyFilterModel())
411 self.__historyCompleter = HistoryCompleter(
412 self.__historyCompletionModel, self)
413 self.__historyCompleter.activated[str].connect(self.__pathSelected)
414 urlbar.setCompleter(self.__historyCompleter)
415 urlbar.returnPressed.connect(
416 lambda: self.__lineEditReturnPressed(urlbar))
417 if position == -1:
418 self.__stackedUrlBar.addWidget(urlbar)
419 else:
420 self.__stackedUrlBar.insertWidget(position, urlbar)
421
422 browser = WebBrowserView(self.__mainWindow, self)
423 urlbar.setBrowser(browser)
424
425 browser.sourceChanged.connect(
426 lambda url: self.__sourceChanged(url, browser))
427 browser.titleChanged.connect(
428 lambda title: self.__titleChanged(title, browser))
429 browser.highlighted.connect(self.showMessage)
430 browser.backwardAvailable.connect(
431 self.__mainWindow.setBackwardAvailable)
432 browser.forwardAvailable.connect(self.__mainWindow.setForwardAvailable)
433 browser.loadStarted.connect(
434 lambda: self.__loadStarted(browser))
435 browser.loadFinished.connect(
436 lambda ok: self.__loadFinished(ok, browser))
437 browser.faviconChanged.connect(
438 lambda: self.__iconChanged(browser))
439 browser.search.connect(self.newBrowser)
440 browser.page().windowCloseRequested.connect(
441 lambda: self.__windowCloseRequested(browser.page()))
442 browser.zoomValueChanged.connect(self.browserZoomValueChanged)
443 if hasattr(WebBrowserPage, "recentlyAudibleChanged"):
444 browser.page().recentlyAudibleChanged.connect(
445 lambda audible: self.__recentlyAudibleChanged(
446 audible, browser.page()))
447 try:
448 browser.page().printRequested.connect(
449 lambda: self.printBrowser(browser))
450 except AttributeError:
451 # pre Qt 5.12
452 pass
453
454 if position == -1:
455 index = self.addTab(browser, self.tr("..."))
456 else:
457 index = self.insertTab(position, browser, self.tr("..."))
458 if not background:
459 self.setCurrentIndex(index)
460
461 self.__mainWindow.closeAct.setEnabled(True)
462 self.__mainWindow.closeAllAct.setEnabled(True)
463 self.__closeButton.setEnabled(True)
464 self.__navigationButton.setEnabled(True)
465
466 if not restoreSession:
467 if not linkName:
468 if Preferences.getWebBrowser("NewTabBehavior") == 0:
469 linkName = "about:blank"
470 elif Preferences.getWebBrowser("NewTabBehavior") == 1:
471 linkName = Preferences.getWebBrowser("HomePage")
472 elif Preferences.getWebBrowser("NewTabBehavior") == 2:
473 linkName = "eric:speeddial"
474
475 if linkName == "eric:blank":
476 linkName = "about:blank"
477
478 if linkName:
479 browser.setSource(QUrl(linkName))
480 if not browser.documentTitle():
481 self.setTabText(index, self.__elide(linkName, Qt.ElideMiddle))
482 self.setTabToolTip(index, linkName)
483 else:
484 self.setTabText(
485 index,
486 self.__elide(browser.documentTitle().replace("&", "&&")))
487 self.setTabToolTip(index, browser.documentTitle())
488
489 self.browserOpened.emit(browser)
490
491 return browser
492
493 def newBrowserAfter(self, browser, link=None, background=False):
494 """
495 Public method to create a new web browser tab after a given one.
496
497 @param browser reference to the browser to add after (WebBrowserView)
498 @param link link to be shown (string or QUrl)
499 @keyparam background flag indicating to open the tab in the
500 background (bool)
501 @return reference to the new browser
502 @rtype WebBrowserView
503 """
504 if browser:
505 position = self.indexOf(browser) + 1
506 else:
507 position = -1
508 return self.newBrowser(link, position, background)
509
510 def __showNavigationMenu(self):
511 """
512 Private slot to show the navigation button menu.
513 """
514 self.__navigationMenu.clear()
515 for index in range(self.count()):
516 act = self.__navigationMenu.addAction(
517 self.tabIcon(index), self.tabText(index))
518 act.setData(index)
519
520 def __navigationMenuTriggered(self, act):
521 """
522 Private slot called to handle the navigation button menu selection.
523
524 @param act reference to the selected action (QAction)
525 """
526 index = act.data()
527 if index is not None:
528 self.setCurrentIndex(index)
529
530 def __windowCloseRequested(self, page):
531 """
532 Private slot to handle the windowCloseRequested signal of a browser.
533
534 @param page reference to the web page
535 @type WebBrowserPage
536 """
537 browser = page.view()
538 if browser is None:
539 return
540
541 index = self.indexOf(browser)
542 self.closeBrowserAt(index)
543
544 def reloadAllBrowsers(self):
545 """
546 Public slot to reload all browsers.
547 """
548 for index in range(self.count()):
549 browser = self.widget(index)
550 browser and browser.reload()
551
552 @pyqtSlot()
553 def closeBrowser(self):
554 """
555 Public slot called to handle the close action.
556 """
557 self.closeBrowserAt(self.currentIndex())
558
559 def closeAllBrowsers(self, shutdown=False):
560 """
561 Public slot called to handle the close all action.
562
563 @param shutdown flag indicating a shutdown action
564 @type bool
565 """
566 for index in range(self.count() - 1, -1, -1):
567 self.closeBrowserAt(index, shutdown=shutdown)
568
569 def closeBrowserView(self, browser):
570 """
571 Public method to close the given browser.
572
573 @param browser reference to the web browser view to be closed
574 @type WebBrowserView
575 """
576 index = self.indexOf(browser)
577 self.closeBrowserAt(index)
578
579 def closeBrowserAt(self, index, shutdown=False):
580 """
581 Public slot to close a browser based on its index.
582
583 @param index index of browser to close
584 @type int
585 @param shutdown flag indicating a shutdown action
586 @type bool
587 """
588 browser = self.widget(index)
589 if browser is None:
590 return
591
592 urlbar = self.__stackedUrlBar.widget(index)
593 self.__stackedUrlBar.removeWidget(urlbar)
594 urlbar.deleteLater()
595 del urlbar
596
597 self.__closedTabsManager.recordBrowser(browser, index)
598
599 browser.closeWebInspector()
600 WebInspector.unregisterView(browser)
601 self.removeTab(index)
602 self.browserClosed.emit(browser)
603 browser.deleteLater()
604 del browser
605
606 if self.count() == 0 and not shutdown:
607 self.newBrowser()
608 else:
609 self.currentChanged[int].emit(self.currentIndex())
610
611 def currentBrowser(self):
612 """
613 Public method to get a reference to the current browser.
614
615 @return reference to the current browser (WebBrowserView)
616 """
617 return self.currentWidget()
618
619 def browserAt(self, index):
620 """
621 Public method to get a reference to the browser with the given index.
622
623 @param index index of the browser to get (integer)
624 @return reference to the indexed browser (WebBrowserView)
625 """
626 return self.widget(index)
627
628 def browsers(self):
629 """
630 Public method to get a list of references to all browsers.
631
632 @return list of references to browsers (list of WebBrowserView)
633 """
634 li = []
635 for index in range(self.count()):
636 li.append(self.widget(index))
637 return li
638
639 @pyqtSlot()
640 def printBrowser(self, browser=None):
641 """
642 Public slot called to print the displayed page.
643
644 @param browser reference to the browser to be printed (WebBrowserView)
645 """
646 if browser is None:
647 browser = self.currentBrowser()
648
649 printer = QPrinter(mode=QPrinter.HighResolution)
650 if Preferences.getPrinter("ColorMode"):
651 printer.setColorMode(QPrinter.Color)
652 else:
653 printer.setColorMode(QPrinter.GrayScale)
654 if Preferences.getPrinter("FirstPageFirst"):
655 printer.setPageOrder(QPrinter.FirstPageFirst)
656 else:
657 printer.setPageOrder(QPrinter.LastPageFirst)
658 printer.setPageMargins(
659 Preferences.getPrinter("LeftMargin") * 10,
660 Preferences.getPrinter("TopMargin") * 10,
661 Preferences.getPrinter("RightMargin") * 10,
662 Preferences.getPrinter("BottomMargin") * 10,
663 QPrinter.Millimeter
664 )
665 printerName = Preferences.getPrinter("PrinterName")
666 if printerName:
667 printer.setPrinterName(printerName)
668 printer.setResolution(Preferences.getPrinter("Resolution"))
669 documentName = WebBrowserTools.getFileNameFromUrl(browser.url())
670 printer.setDocName(documentName)
671
672 printDialog = QPrintDialog(printer, self)
673 printDialog.setOptions(QAbstractPrintDialog.PrintToFile |
674 QAbstractPrintDialog.PrintShowPageSize)
675 if not Globals.isWindowsPlatform():
676 if FilePrinter.isCupsAvailable():
677 printDialog.setOption(QAbstractPrintDialog.PrintCollateCopies)
678 printDialog.setOption(QAbstractPrintDialog.PrintPageRange)
679 if printDialog.exec_() == QDialog.Accepted:
680 if hasattr(browser.page(), "print"):
681 # Qt >= 5.8.0
682 browser.page().execPrintPage(printer, 10 * 1000)
683 elif hasattr(browser.page(), "printToPdf"):
684 # Qt >= 5.7.0
685 if printer.outputFormat() == QPrinter.PdfFormat:
686 # print to PDF file selected
687 browser.page().printToPdf(
688 lambda pdf: self.__pdfGeneratedForSave(
689 printer.outputFileName(), pdf),
690 printer.pageLayout())
691 else:
692 # print to printer
693 self.__pdfPrinter = printer
694 browser.page().printToPdf(
695 self.__pdfGeneratedForPrinting,
696 printer.pageLayout())
697 else:
698 browser.render(printer)
699
700 @pyqtSlot()
701 def printBrowserPdf(self, browser=None):
702 """
703 Public slot called to print the displayed page to PDF.
704
705 @param browser reference to the browser to be printed (HelpBrowser)
706 """
707 if browser is None:
708 browser = self.currentBrowser()
709
710 name = WebBrowserTools.getFileNameFromUrl(browser.url())
711 if name:
712 name = name.rsplit('.', 1)[0]
713 name += '.pdf'
714 if hasattr(browser.page(), "printToPdf"):
715 from .Tools.PrintToPdfDialog import PrintToPdfDialog
716 if not name:
717 name = "printout.pdf"
718 dlg = PrintToPdfDialog(name, self)
719 if dlg.exec_() == QDialog.Accepted:
720 filePath, pageLayout = dlg.getData()
721 if filePath:
722 if os.path.exists(filePath):
723 res = E5MessageBox.warning(
724 self,
725 self.tr("Print to PDF"),
726 self.tr("""<p>The file <b>{0}</b> exists"""
727 """ already. Shall it be"""
728 """ overwritten?</p>""").format(filePath),
729 E5MessageBox.StandardButtons(
730 E5MessageBox.No |
731 E5MessageBox.Yes),
732 E5MessageBox.No)
733 if res == E5MessageBox.No:
734 return
735 browser.page().printToPdf(
736 lambda pdf: self.__pdfGeneratedForSave(filePath, pdf),
737 pageLayout)
738 elif Globals.isLinuxPlatform():
739 printer = QPrinter(mode=QPrinter.HighResolution)
740 if Preferences.getPrinter("ColorMode"):
741 printer.setColorMode(QPrinter.Color)
742 else:
743 printer.setColorMode(QPrinter.GrayScale)
744 printerName = Preferences.getPrinter("PrinterName")
745 if printerName:
746 printer.setPrinterName(printerName)
747 printer.setOutputFormat(QPrinter.PdfFormat)
748 if name:
749 printer.setOutputFileName(name)
750 printer.setResolution(Preferences.getPrinter("Resolution"))
751
752 printDialog = QPrintDialog(printer, self)
753 if printDialog.exec_() == QDialog.Accepted:
754 browser.render(printer)
755
756 def __pdfGeneratedForSave(self, filePath, pdfData):
757 """
758 Private slot to save the generated PDF data to a file.
759
760 @param filePath path to save the PDF to
761 @type str
762 @param pdfData generated PDF document
763 @type QByteArray
764 """
765 if pdfData.size() == 0:
766 return
767
768 pdfFile = QFile(filePath)
769 if pdfFile.open(QFile.WriteOnly):
770 pdfFile.write(pdfData)
771 pdfFile.close()
772 if pdfFile.error() != QFileDevice.NoError:
773 E5MessageBox.critical(
774 self,
775 self.tr("Print to PDF"),
776 self.tr("""<p>The PDF could not be written to file <b>{0}"""
777 """</b>.</p><p><b>Error:</b> {1}</p>""").format(
778 filePath, pdfFile.errorString()),
779 E5MessageBox.StandardButtons(
780 E5MessageBox.Ok))
781
782 def __pdfGeneratedForPrinting(self, pdfData):
783 """
784 Private slot to print the generated PDF data.
785
786 @param pdfData generated PDF document
787 @type QByteArray
788 """
789 if self.__pdfPrinter is None or pdfData.isEmpty():
790 return
791
792 tempFile = QTemporaryFile(QDir.tempPath() + "/ericBrowserXXXXXX.pdf")
793 tempFile.setAutoRemove(False)
794 if tempFile.open():
795 bytesWritten = tempFile.write(pdfData)
796 tempFile.close()
797 if bytesWritten == pdfData.size():
798 if Globals.isWindowsPlatform():
799 printerName = self.__pdfPrinter.printerName()
800 import ctypes
801 ctypes.windll.shell32.ShellExecuteW(
802 None, "printto", tempFile.fileName(),
803 '"{0}"'.format(printerName), None, 0)
804 else:
805 FilePrinter.printFile(
806 self.__pdfPrinter, tempFile.fileName(),
807 FilePrinter.FilePrinter.SystemDeletesFiles,
808 FilePrinter.FilePrinter.SystemSelectsPages)
809 else:
810 tempFile.remove()
811
812 self.__pdfPrinter = None
813
814 @pyqtSlot()
815 def printPreviewBrowser(self, browser=None):
816 """
817 Public slot called to show a print preview of the displayed file.
818
819 @param browser reference to the browser to be printed (WebBrowserView)
820 """
821 from PyQt5.QtPrintSupport import QPrintPreviewDialog
822
823 if browser is None:
824 browser = self.currentBrowser()
825
826 printer = QPrinter(mode=QPrinter.HighResolution)
827 if Preferences.getPrinter("ColorMode"):
828 printer.setColorMode(QPrinter.Color)
829 else:
830 printer.setColorMode(QPrinter.GrayScale)
831 if Preferences.getPrinter("FirstPageFirst"):
832 printer.setPageOrder(QPrinter.FirstPageFirst)
833 else:
834 printer.setPageOrder(QPrinter.LastPageFirst)
835 printer.setPageMargins(
836 Preferences.getPrinter("LeftMargin") * 10,
837 Preferences.getPrinter("TopMargin") * 10,
838 Preferences.getPrinter("RightMargin") * 10,
839 Preferences.getPrinter("BottomMargin") * 10,
840 QPrinter.Millimeter
841 )
842 printerName = Preferences.getPrinter("PrinterName")
843 if printerName:
844 printer.setPrinterName(printerName)
845 printer.setResolution(Preferences.getPrinter("Resolution"))
846
847 preview = QPrintPreviewDialog(printer, self)
848 preview.resize(800, 750)
849 if qVersionTuple() >= (5, 8, 0):
850 preview.paintRequested.connect(
851 lambda p: self.__printPreviewRequested(p, browser))
852 else:
853 preview.paintRequested.connect(lambda p: browser.render(p))
854 preview.exec_()
855
856 def __printPreviewRequested(self, printer, browser):
857 """
858 Private slot to generate the print preview.
859
860 @param printer reference to the printer object
861 @type QPrinter
862 @param browser reference to the browser to be printed
863 @type WebBrowserView
864 """
865 QApplication.setOverrideCursor(Qt.WaitCursor)
866 browser.page().execPrintPage(printer, 10 * 1000)
867 QApplication.restoreOverrideCursor()
868
869 def __sourceChanged(self, url, browser):
870 """
871 Private slot to handle a change of a browsers source.
872
873 @param url URL of the new site
874 @type QUrl
875 @param browser reference to the web browser
876 @type WebBrowserView
877 """
878 self.sourceChanged.emit(browser, url)
879
880 if browser == self.currentBrowser():
881 self.currentUrlChanged.emit(url)
882
883 def __titleChanged(self, title, browser):
884 """
885 Private slot to handle a change of a browsers title.
886
887 @param title new title
888 @type str
889 @param browser reference to the web browser
890 @type WebBrowserView
891 """
892 index = self.indexOf(browser)
893 if title == "":
894 title = browser.url().toString()
895
896 self.setTabText(index, self.__elide(title.replace("&", "&&")))
897 self.setTabToolTip(index, title)
898
899 self.titleChanged.emit(browser, title)
900
901 def __elide(self, txt, mode=Qt.ElideRight, length=40):
902 """
903 Private method to elide some text.
904
905 @param txt text to be elided (string)
906 @keyparam mode elide mode (Qt.TextElideMode)
907 @keyparam length amount of characters to be used (integer)
908 @return the elided text (string)
909 """
910 if mode == Qt.ElideNone or len(txt) < length:
911 return txt
912 elif mode == Qt.ElideLeft:
913 return "...{0}".format(txt[-length:])
914 elif mode == Qt.ElideMiddle:
915 return "{0}...{1}".format(txt[:length // 2], txt[-(length // 2):])
916 elif mode == Qt.ElideRight:
917 return "{0}...".format(txt[:length])
918 else:
919 # just in case
920 return txt
921
922 def preferencesChanged(self):
923 """
924 Public slot to handle a change of preferences.
925 """
926 for browser in self.browsers():
927 browser.preferencesChanged()
928
929 for urlbar in self.__stackedUrlBar.urlBars():
930 urlbar.preferencesChanged()
931
932 if Preferences.getUI("SingleCloseButton"):
933 self.setTabsClosable(False)
934 try:
935 self.tabCloseRequested.disconnect(self.closeBrowserAt)
936 except TypeError:
937 pass
938 self.__closeButton.show()
939 else:
940 self.setTabsClosable(True)
941 self.tabCloseRequested.connect(self.closeBrowserAt)
942 self.__closeButton.hide()
943
944 def __loadStarted(self, browser):
945 """
946 Private method to handle the loadStarted signal.
947
948 @param browser reference to the web browser
949 @type WebBrowserView
950 """
951 index = self.indexOf(browser)
952 anim = self.animationLabel(
953 index, os.path.join(getConfig("ericPixDir"), "loading.gif"),
954 100)
955 if not anim:
956 loading = QIcon(os.path.join(getConfig("ericPixDir"),
957 "loading.gif"))
958 self.setTabIcon(index, loading)
959 else:
960 self.setTabIcon(index, QIcon())
961 self.setTabText(index, self.tr("Loading..."))
962 self.setTabToolTip(index, self.tr("Loading..."))
963 self.showMessage.emit(self.tr("Loading..."))
964
965 self.__mainWindow.setLoadingActions(True)
966
967 def __loadFinished(self, ok, browser):
968 """
969 Private method to handle the loadFinished signal.
970
971 @param ok flag indicating the result
972 @type bool
973 @param browser reference to the web browser
974 @type WebBrowserView
975 """
976 import WebBrowser.WebBrowserWindow
977 index = self.indexOf(browser)
978 self.resetAnimation(index)
979 self.setTabIcon(
980 index, WebBrowser.WebBrowserWindow.WebBrowserWindow.icon(
981 browser.url()))
982 if ok:
983 self.showMessage.emit(self.tr("Finished loading"))
984 else:
985 self.showMessage.emit(self.tr("Failed to load"))
986
987 self.__mainWindow.setLoadingActions(False)
988
989 def __iconChanged(self, browser):
990 """
991 Private slot to handle a change of the web site icon.
992
993 @param browser reference to the web browser
994 @type WebBrowserView
995 """
996 self.setTabIcon(
997 self.indexOf(browser),
998 browser.icon())
999 self.__mainWindow.bookmarksManager().faviconChanged(browser.url())
1000
1001 def getSourceFileList(self):
1002 """
1003 Public method to get a list of all opened Qt help files.
1004
1005 @return dictionary with tab id as key and host/namespace as value
1006 """
1007 sourceList = {}
1008 for i in range(self.count()):
1009 browser = self.widget(i)
1010 if browser is not None and \
1011 browser.source().isValid():
1012 sourceList[i] = browser.source().host()
1013
1014 return sourceList
1015
1016 def shallShutDown(self):
1017 """
1018 Public method to check, if the application should be shut down.
1019
1020 @return flag indicating a shut down (boolean)
1021 """
1022 if self.count() > 1 and Preferences.getWebBrowser(
1023 "WarnOnMultipleClose"):
1024 mb = E5MessageBox.E5MessageBox(
1025 E5MessageBox.Information,
1026 self.tr("Are you sure you want to close the window?"),
1027 self.tr("""Are you sure you want to close the window?\n"""
1028 """You have %n tab(s) open.""", "", self.count()),
1029 modal=True,
1030 parent=self)
1031 quitButton = mb.addButton(
1032 self.tr("&Quit"), E5MessageBox.AcceptRole)
1033 quitButton.setIcon(UI.PixmapCache.getIcon("exit.png"))
1034 closeTabButton = mb.addButton(
1035 self.tr("C&lose Current Tab"), E5MessageBox.AcceptRole)
1036 closeTabButton.setIcon(UI.PixmapCache.getIcon("tabClose.png"))
1037 mb.addButton(E5MessageBox.Cancel)
1038 mb.exec_()
1039 if mb.clickedButton() == quitButton:
1040 return True
1041 else:
1042 if mb.clickedButton() == closeTabButton:
1043 self.closeBrowser()
1044 return False
1045
1046 return True
1047
1048 def stackedUrlBar(self):
1049 """
1050 Public method to get a reference to the stacked url bar.
1051
1052 @return reference to the stacked url bar (StackedUrlBar)
1053 """
1054 return self.__stackedUrlBar
1055
1056 def currentUrlBar(self):
1057 """
1058 Public method to get a reference to the current url bar.
1059
1060 @return reference to the current url bar (UrlBar)
1061 """
1062 return self.__stackedUrlBar.currentWidget()
1063
1064 def urlBarForView(self, view):
1065 """
1066 Public method to get a reference to the UrlBar associated with the
1067 given view.
1068
1069 @param view reference to the view to get the urlbar for
1070 @type WebBrowserView
1071 @return reference to the associated urlbar
1072 @rtype UrlBar
1073 """
1074 for urlbar in self.__stackedUrlBar.urlBars():
1075 if urlbar.browser() is view:
1076 return urlbar
1077
1078 return None
1079
1080 def __lineEditReturnPressed(self, edit):
1081 """
1082 Private slot to handle the entering of an URL.
1083
1084 @param edit reference to the line edit
1085 @type UrlBar
1086 """
1087 url = self.__guessUrlFromPath(edit.text())
1088 if e5App().keyboardModifiers() == Qt.AltModifier:
1089 self.newBrowser(url)
1090 else:
1091 self.currentBrowser().setSource(url)
1092 self.currentBrowser().setFocus()
1093
1094 def __pathSelected(self, path):
1095 """
1096 Private slot called when a URL is selected from the completer.
1097
1098 @param path path to be shown (string)
1099 """
1100 url = self.__guessUrlFromPath(path)
1101 self.currentBrowser().setSource(url)
1102
1103 def __guessUrlFromPath(self, path):
1104 """
1105 Private method to guess an URL given a path string.
1106
1107 @param path path string to guess an URL for (string)
1108 @return guessed URL (QUrl)
1109 """
1110 manager = self.__mainWindow.openSearchManager()
1111 path = Utilities.fromNativeSeparators(path)
1112 url = manager.convertKeywordSearchToUrl(path)
1113 if url.isValid():
1114 return url
1115
1116 try:
1117 url = QUrl.fromUserInput(path)
1118 except AttributeError:
1119 url = QUrl(path)
1120
1121 if url.scheme() == "about" and \
1122 url.path() == "home":
1123 url = QUrl("eric:home")
1124
1125 if url.scheme() in ["s", "search"]:
1126 url = manager.currentEngine().searchUrl(url.path().strip())
1127
1128 if url.scheme() != "" and \
1129 (url.host() != "" or url.path() != ""):
1130 return url
1131
1132 urlString = Preferences.getWebBrowser("DefaultScheme") + path.strip()
1133 url = QUrl.fromEncoded(urlString.encode("utf-8"), QUrl.TolerantMode)
1134
1135 return url
1136
1137 def __currentChanged(self, index):
1138 """
1139 Private slot to handle an index change.
1140
1141 @param index new index (integer)
1142 """
1143 self.__stackedUrlBar.setCurrentIndex(index)
1144
1145 browser = self.browserAt(index)
1146 if browser is not None:
1147 if browser.url() == "" and browser.hasFocus():
1148 self.__stackedUrlBar.currentWidget.setFocus()
1149 elif browser.url() != "":
1150 browser.setFocus()
1151
1152 def restoreClosedTab(self, act):
1153 """
1154 Public slot to restore the most recently closed tab.
1155
1156 @param act reference to the action that triggered
1157 @type QAction
1158 """
1159 if not self.canRestoreClosedTab():
1160 return
1161
1162 tab = self.__closedTabsManager.getClosedTabAt(act.data())
1163
1164 self.newBrowser(tab.url.toString(), position=tab.position)
1165
1166 def canRestoreClosedTab(self):
1167 """
1168 Public method to check, if closed tabs can be restored.
1169
1170 @return flag indicating that closed tabs can be restored (boolean)
1171 """
1172 return self.__closedTabsManager.isClosedTabAvailable()
1173
1174 def restoreAllClosedTabs(self):
1175 """
1176 Public slot to restore all closed tabs.
1177 """
1178 if not self.canRestoreClosedTab():
1179 return
1180
1181 for tab in self.__closedTabsManager.allClosedTabs():
1182 self.newBrowser(tab.url.toString(), position=tab.position)
1183 self.__closedTabsManager.clearList()
1184
1185 def clearClosedTabsList(self):
1186 """
1187 Public slot to clear the list of closed tabs.
1188 """
1189 self.__closedTabsManager.clearList()
1190
1191 def __aboutToShowClosedTabsMenu(self):
1192 """
1193 Private slot to populate the closed tabs menu.
1194 """
1195 fm = self.__closedTabsMenu.fontMetrics()
1196 maxWidth = fm.width('m') * 40
1197
1198 self.__closedTabsMenu.clear()
1199 index = 0
1200 for tab in self.__closedTabsManager.allClosedTabs():
1201 title = fm.elidedText(tab.title, Qt.ElideRight, maxWidth)
1202 act = self.__closedTabsMenu.addAction(
1203 self.__mainWindow.icon(tab.url), title)
1204 act.setData(index)
1205 act.triggered.connect(lambda: self.restoreClosedTab(act))
1206 index += 1
1207 self.__closedTabsMenu.addSeparator()
1208 self.__closedTabsMenu.addAction(
1209 self.tr("Restore All Closed Tabs"), self.restoreAllClosedTabs)
1210 self.__closedTabsMenu.addAction(
1211 self.tr("Clear List"), self.clearClosedTabsList)
1212
1213 def closedTabsManager(self):
1214 """
1215 Public slot to get a reference to the closed tabs manager.
1216
1217 @return reference to the closed tabs manager (ClosedTabsManager)
1218 """
1219 return self.__closedTabsManager
1220
1221 def __closedTabAvailable(self, avail):
1222 """
1223 Private slot to handle changes of the availability of closed tabs.
1224
1225 @param avail flag indicating the availability of closed tabs (boolean)
1226 """
1227 self.__closedTabsButton.setEnabled(avail)
1228 self.__restoreClosedTabAct.setEnabled(avail)
1229
1230 ####################################################
1231 ## Methods below implement session related functions
1232 ####################################################
1233
1234 def getSessionData(self):
1235 """
1236 Public method to populate the session data.
1237
1238 @return dictionary containing the session data
1239 @rtype dict
1240 """
1241 sessionData = {}
1242
1243 # 1. current index
1244 sessionData["CurrentTabIndex"] = self.currentIndex()
1245
1246 # 2. tab data
1247 sessionData["Tabs"] = []
1248 for index in range(self.count()):
1249 browser = self.widget(index)
1250 data = browser.getSessionData()
1251 sessionData["Tabs"].append(data)
1252
1253 return sessionData
1254
1255 def loadFromSessionData(self, sessionData):
1256 """
1257 Public method to load the session data.
1258
1259 @param sessionData dictionary containing the session data as
1260 generated by getSessionData()
1261 @type dict
1262 """
1263 tabCount = self.count()
1264
1265 # 1. load tab data
1266 if "Tabs" in sessionData:
1267 loadTabOnActivate = \
1268 Preferences.getWebBrowser("LoadTabOnActivation")
1269 for data in sessionData["Tabs"]:
1270 browser = self.newBrowser(restoreSession=True)
1271 if loadTabOnActivate:
1272 browser.storeSessionData(data)
1273 title, urlStr, icon = browser.extractSessionMetaData(data)
1274 index = self.indexOf(browser)
1275 self.setTabText(index, title)
1276 self.setTabIcon(index, icon)
1277 else:
1278 browser.loadFromSessionData(data)
1279
1280 # 2. set tab index
1281 if "CurrentTabIndex" in sessionData and \
1282 sessionData["CurrentTabIndex"] >= 0:
1283 index = tabCount + sessionData["CurrentTabIndex"]
1284 self.setCurrentIndex(index)
1285 self.browserAt(index).activateSession()

eric ide

mercurial