Advertisement
Guest User

PySide2 tabbedbrowswer

a guest
Mar 20th, 2019
358
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 41.79 KB | None | 0 0
  1. #############################################################################
  2. ##
  3. ## Copyright (C) 2018 The Qt Company Ltd.
  4. ## Contact: http://www.qt.io/licensing/
  5. ##
  6. ## This file is part of the Qt for Python examples of the Qt Toolkit.
  7. ##
  8. ## $QT_BEGIN_LICENSE:BSD$
  9. ## You may use this file under the terms of the BSD license as follows:
  10. ##
  11. ## "Redistribution and use in source and binary forms, with or without
  12. ## modification, are permitted provided that the following conditions are
  13. ## met:
  14. ##   * Redistributions of source code must retain the above copyright
  15. ##     notice, this list of conditions and the following disclaimer.
  16. ##   * Redistributions in binary form must reproduce the above copyright
  17. ##     notice, this list of conditions and the following disclaimer in
  18. ##     the documentation and/or other materials provided with the
  19. ##     distribution.
  20. ##   * Neither the name of The Qt Company Ltd nor the names of its
  21. ##     contributors may be used to endorse or promote products derived
  22. ##     from this software without specific prior written permission.
  23. ##
  24. ##
  25. ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  26. ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  27. ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  28. ## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  29. ## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  30. ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  31. ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  32. ## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  33. ## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34. ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  35. ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  36. ##
  37. ## $QT_END_LICENSE$
  38. ##
  39. #############################################################################
  40.  
  41. import json, os, warnings
  42.  
  43. from PySide2 import QtCore
  44. from PySide2.QtCore import (QDir, QFileInfo, QModelIndex, QStandardPaths, Qt,
  45.     QUrl)
  46. from PySide2.QtGui import QIcon, QPixmap, QStandardItem, QStandardItemModel
  47. from PySide2.QtWidgets import (QAction, QDockWidget, QMenu, QMessageBox,
  48.     QToolBar, QTreeView, QWidget)
  49.  
  50. _url_role = Qt.UserRole + 1
  51.  
  52. # Default bookmarks as an array of arrays which is the form
  53. # used to read from/write to a .json bookmarks file
  54. _default_bookmarks = [
  55.     ['Tool Bar'],
  56.     ['http://qt.io', 'Qt', ':/qt-project.org/qmessagebox/images/qtlogo-64.png'],
  57.     ['https://download.qt.io/snapshots/ci/pyside/', 'Downloads'],
  58.     ['https://doc-snapshots.qt.io/qtforpython/', 'Documentation'],
  59.     ['https://bugreports.qt.io/projects/PYSIDE/', 'Bug Reports'],
  60.     ['https://www.python.org/', 'Python', None],
  61.     ['https://wiki.qt.io/PySide2', 'Qt for Python', None],
  62.     ['Other Bookmarks']
  63. ]
  64.  
  65. def _config_dir():
  66.     return '{}/QtForPythonBrowser'.format(
  67.         QStandardPaths.writableLocation(QStandardPaths.ConfigLocation))
  68.  
  69. _bookmark_file = 'bookmarks.json'
  70.  
  71. def _create_folder_item(title):
  72.     result = QStandardItem(title)
  73.     result.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
  74.     return result
  75.  
  76. def _create_item(url, title, icon):
  77.     result = QStandardItem(title)
  78.     result.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
  79.     result.setData(url, _url_role)
  80.     if icon is not None:
  81.         result.setIcon(icon)
  82.     return result
  83.  
  84. # Create the model from an array of arrays
  85. def _create_model(parent, serialized_bookmarks):
  86.     result = QStandardItemModel(0, 1, parent)
  87.     last_folder_item = None
  88.     for entry in serialized_bookmarks:
  89.         if len(entry) == 1:
  90.             last_folder_item = _create_folder_item(entry[0])
  91.             result.appendRow(last_folder_item)
  92.         else:
  93.             url = QUrl.fromUserInput(entry[0])
  94.             title = entry[1]
  95.             icon = QIcon(entry[2]) if len(entry) > 2 and entry[2] else None
  96.             last_folder_item.appendRow(_create_item(url, title, icon))
  97.     return result
  98.  
  99. # Serialize model into an array of arrays, writing out the icons
  100. # into .png files under directory in the process
  101. def _serialize_model(model, directory):
  102.     result = []
  103.     folder_count = model.rowCount()
  104.     for f in range(0, folder_count):
  105.         folder_item = model.item(f)
  106.         result.append([folder_item.text()])
  107.         item_count = folder_item.rowCount()
  108.         for i in range(0, item_count):
  109.             item = folder_item.child(i)
  110.             entry = [item.data(_url_role).toString(), item.text()]
  111.             icon = item.icon()
  112.             if not icon.isNull():
  113.                 icon_sizes = icon.availableSizes()
  114.                 largest_size = icon_sizes[len(icon_sizes) - 1]
  115.                 icon_file_name = '{}/icon{:02}_{:02}_{}.png'.format(directory,
  116.                                f, i, largest_size.width())
  117.                 icon.pixmap(largest_size).save(icon_file_name, 'PNG')
  118.                 entry.append(icon_file_name)
  119.             result.append(entry)
  120.     return result
  121.  
  122. # Bookmarks as a tree view to be used in a dock widget with
  123. # functionality to persist and populate tool bars and menus.
  124. class BookmarkWidget(QTreeView):
  125.     """Provides a tree view to manage the bookmarks."""
  126.  
  127.     open_bookmark = QtCore.Signal(QUrl)
  128.     open_bookmark_in_new_tab = QtCore.Signal(QUrl)
  129.     changed = QtCore.Signal()
  130.  
  131.     def __init__(self):
  132.         super(BookmarkWidget, self).__init__()
  133.         self.setRootIsDecorated(False)
  134.         self.setUniformRowHeights(True)
  135.         self.setHeaderHidden(True)
  136.         self._model = _create_model(self, self._read_bookmarks())
  137.         self.setModel(self._model)
  138.         self.expandAll()
  139.         self.activated.connect(self._activated)
  140.         self._model.rowsInserted.connect(self._changed)
  141.         self._model.rowsRemoved.connect(self._changed)
  142.         self._model.dataChanged.connect(self._changed)
  143.         self._modified = False
  144.  
  145.     def _changed(self):
  146.         self._modified = True
  147.         self.changed.emit()
  148.  
  149.     def _activated(self, index):
  150.         item = self._model.itemFromIndex(index)
  151.         self.open_bookmark.emit(item.data(_url_role))
  152.  
  153.     def _action_activated(self, index):
  154.         action = self.sender()
  155.         self.open_bookmark.emit(action.data())
  156.  
  157.     def _tool_bar_item(self):
  158.         return self._model.item(0, 0)
  159.  
  160.     def _other_item(self):
  161.         return self._model.item(1, 0)
  162.  
  163.     def add_bookmark(self, url, title, icon):
  164.         self._other_item().appendRow(_create_item(url, title, icon))
  165.  
  166.     def add_tool_bar_bookmark(self, url, title, icon):
  167.         self._tool_bar_item().appendRow(_create_item(url, title, icon))
  168.  
  169.     # Synchronize the bookmarks under parent_item to a target_object
  170.     # like QMenu/QToolBar, which has a list of actions. Update
  171.     # the existing actions, append new ones if needed or hide
  172.     # superfluous ones
  173.     def _populate_actions(self, parent_item, target_object, first_action):
  174.         existing_actions = target_object.actions()
  175.         existing_action_count = len(existing_actions)
  176.         a = first_action
  177.         row_count = parent_item.rowCount()
  178.         for r in range(0, row_count):
  179.             item = parent_item.child(r)
  180.             title = item.text()
  181.             icon = item.icon()
  182.             url = item.data(_url_role)
  183.             if a < existing_action_count:
  184.                 action = existing_actions[a]
  185.                 if (title != action.toolTip()):
  186.                     action.setText(BookmarkWidget.short_title(title))
  187.                     action.setIcon(icon)
  188.                     action.setToolTip(title)
  189.                     action.setData(url)
  190.                     action.setVisible(True)
  191.             else:
  192.                 action = target_object.addAction(icon, BookmarkWidget.short_title(title))
  193.                 action.setToolTip(title)
  194.                 action.setData(url)
  195.                 action.triggered.connect(self._action_activated)
  196.             a = a + 1
  197.         while a < existing_action_count:
  198.             existing_actions[a].setVisible(False)
  199.             a = a + 1
  200.  
  201.     def populate_tool_bar(self, tool_bar):
  202.         self._populate_actions(self._tool_bar_item(), tool_bar, 0)
  203.  
  204.     def populate_other(self, menu, first_action):
  205.         self._populate_actions(self._other_item(), menu, first_action)
  206.  
  207.     def _current_item(self):
  208.         index = self.currentIndex()
  209.         if index.isValid():
  210.             item = self._model.itemFromIndex(index)
  211.             if item.parent(): # exclude top level items
  212.                 return item
  213.         return None
  214.  
  215.     def context_menu_event(self, event):
  216.         context_menu = QMenu()
  217.         open_in_new_tab_action = context_menu.addAction("Open in New Tab")
  218.         remove_action = context_menu.addAction("Remove...")
  219.         current_item = self._current_item()
  220.         open_in_new_tab_action.setEnabled(current_item is not None)
  221.         remove_action.setEnabled(current_item is not None)
  222.         chosen_action = context_menu.exec_(event.globalPos())
  223.         if chosen_action == open_in_new_tab_action:
  224.             self.open_bookmarkInNewTab.emit(current_item.data(_url_role))
  225.         elif chosen_action == remove_action:
  226.             self._remove_item(current_item)
  227.  
  228.     def _remove_item(self, item):
  229.         button = QMessageBox.question(self, "Remove",
  230.             "Would you like to remove \"{}\"?".format(item.text()),
  231.             QMessageBox.Yes | QMessageBox.No)
  232.         if button == QMessageBox.Yes:
  233.             item.parent().removeRow(item.row())
  234.  
  235.     def write_bookmarks(self):
  236.         if not self._modified:
  237.             return
  238.         dir_path = _config_dir()
  239.         native_dir_path = QDir.toNativeSeparators(dir_path)
  240.         dir = QFileInfo(dir_path)
  241.         if not dir.isDir():
  242.             print('Creating {}...'.format(native_dir_path))
  243.             if not QDir(dir.absolutePath()).mkpath(dir.fileName()):
  244.                 warnings.warn('Cannot create {}.'.format(native_dir_path),
  245.                               RuntimeWarning)
  246.                 return
  247.         serialized_model = _serialize_model(self._model, dir_path)
  248.         bookmark_file_name = os.path.join(native_dir_path, _bookmark_file)
  249.         print('Writing {}...'.format(bookmark_file_name))
  250.         with open(bookmark_file_name, 'w') as bookmark_file:
  251.             json.dump(serialized_model, bookmark_file, indent = 4)
  252.  
  253.     def _read_bookmarks(self):
  254.         bookmark_file_name = os.path.join(QDir.toNativeSeparators(_config_dir()),
  255.                                         _bookmark_file)
  256.         if os.path.exists(bookmark_file_name):
  257.             print('Reading {}...'.format(bookmark_file_name))
  258.             return json.load(open(bookmark_file_name))
  259.         return _default_bookmarks
  260.  
  261.     # Return a short title for a bookmark action,
  262.     # "Qt | Cross Platform.." -> "Qt"
  263.     @staticmethod
  264.     def short_title(t):
  265.         i = t.find(' | ')
  266.         if i == -1:
  267.             i = t.find(' - ')
  268.         return t[0:i] if i != -1 else t
  269.  
  270.  
  271.  
  272.  
  273.  
  274.  
  275. #############################################################################
  276. ##
  277. ## Copyright (C) 2018 The Qt Company Ltd.
  278.  
  279. ##
  280. #############################################################################
  281.  
  282. from functools import partial
  283. import sys
  284.  
  285. from bookmarkwidget import BookmarkWidget
  286. from webengineview import WebEngineView
  287. from PySide2 import QtCore
  288. from PySide2.QtCore import QPoint, Qt, QUrl
  289. from PySide2.QtWidgets import (QAction, QMenu, QTabBar, QTabWidget)
  290. from PySide2.QtWebEngineWidgets import (QWebEngineDownloadItem,
  291.     QWebEnginePage, QWebEngineProfile)
  292.  
  293. class BrowserTabWidget(QTabWidget):
  294.     """Enables having several tabs with QWebEngineView."""
  295.  
  296.     url_changed = QtCore.Signal(QUrl)
  297.     enabled_changed = QtCore.Signal(QWebEnginePage.WebAction, bool)
  298.     download_requested = QtCore.Signal(QWebEngineDownloadItem)
  299.  
  300.     def __init__(self, window_factory_function):
  301.         super(BrowserTabWidget, self).__init__()
  302.         self.setTabsClosable(True)
  303.         self._window_factory_function = window_factory_function
  304.         self._webengineviews = []
  305.         self.currentChanged.connect(self._current_changed)
  306.         self.tabCloseRequested.connect(self.handle_tab_close_request)
  307.         self._actions_enabled = {}
  308.         for web_action in WebEngineView.web_actions():
  309.             self._actions_enabled[web_action] = False
  310.  
  311.         tab_bar = self.tabBar()
  312.         tab_bar.setSelectionBehaviorOnRemove(QTabBar.SelectPreviousTab)
  313.         tab_bar.setContextMenuPolicy(Qt.CustomContextMenu)
  314.         tab_bar.customContextMenuRequested.connect(self._handle_tab_context_menu)
  315.  
  316.     def add_browser_tab(self):
  317.         factory_func = partial(BrowserTabWidget.add_browser_tab, self)
  318.         web_engine_view = WebEngineView(factory_func, self._window_factory_function)
  319.         index = self.count()
  320.         self._webengineviews.append(web_engine_view)
  321.         title = 'Tab {}'.format(index + 1)
  322.         self.addTab(web_engine_view, title)
  323.         page = web_engine_view.page()
  324.         page.titleChanged.connect(self._title_changed)
  325.         page.iconChanged.connect(self._icon_changed)
  326.         page.profile().downloadRequested.connect(self._download_requested)
  327.         web_engine_view.urlChanged.connect(self._url_changed)
  328.         web_engine_view.enabled_changed.connect(self._enabled_changed)
  329.         self.setCurrentIndex(index)
  330.         return web_engine_view
  331.  
  332.     def load(self, url):
  333.         index = self.currentIndex()
  334.         if index >= 0 and url.isValid():
  335.             self._webengineviews[index].setUrl(url)
  336.  
  337.     def find(self, needle, flags):
  338.         index = self.currentIndex()
  339.         if index >= 0:
  340.             self._webengineviews[index].page().findText(needle, flags)
  341.  
  342.     def url(self):
  343.         index = self.currentIndex()
  344.         return self._webengineviews[index].url() if index >= 0 else QUrl()
  345.  
  346.     def _url_changed(self, url):
  347.         index = self.currentIndex()
  348.         if index >= 0 and self._webengineviews[index] == self.sender():
  349.                 self.url_changed.emit(url)
  350.  
  351.     def _title_changed(self, title):
  352.         index = self._index_of_page(self.sender())
  353.         if (index >= 0):
  354.             self.setTabText(index, BookmarkWidget.short_title(title))
  355.  
  356.     def _icon_changed(self, icon):
  357.         index = self._index_of_page(self.sender())
  358.         if (index >= 0):
  359.             self.setTabIcon(index, icon)
  360.  
  361.     def _enabled_changed(self, web_action, enabled):
  362.         index = self.currentIndex()
  363.         if index >= 0 and self._webengineviews[index] == self.sender():
  364.             self._check_emit_enabled_changed(web_action, enabled)
  365.  
  366.     def _check_emit_enabled_changed(self, web_action, enabled):
  367.         if enabled != self._actions_enabled[web_action]:
  368.             self._actions_enabled[web_action] = enabled
  369.             self.enabled_changed.emit(web_action, enabled)
  370.  
  371.     def _current_changed(self, index):
  372.         self._update_actions(index)
  373.         self.url_changed.emit(self.url())
  374.  
  375.     def _update_actions(self, index):
  376.         if index >= 0 and index < len(self._webengineviews):
  377.             view = self._webengineviews[index]
  378.             for web_action in WebEngineView.web_actions():
  379.                 enabled = view.is_web_action_enabled(web_action)
  380.                 self._check_emit_enabled_changed(web_action, enabled)
  381.  
  382.     def back(self):
  383.         self._trigger_action(QWebEnginePage.Back)
  384.  
  385.     def forward(self):
  386.         self._trigger_action(QWebEnginePage.Forward)
  387.  
  388.     def reload(self):
  389.         self._trigger_action(QWebEnginePage.Reload)
  390.  
  391.     def undo(self):
  392.         self._trigger_action(QWebEnginePage.Undo)
  393.  
  394.     def redo(self):
  395.         self._trigger_action(QWebEnginePage.Redo)
  396.  
  397.     def cut(self):
  398.         self._trigger_action(QWebEnginePage.Cut)
  399.  
  400.     def copy(self):
  401.         self._trigger_action(QWebEnginePage.Copy)
  402.  
  403.     def paste(self):
  404.         self._trigger_action(QWebEnginePage.Paste)
  405.  
  406.     def select_all(self):
  407.         self._trigger_action(QWebEnginePage.SelectAll)
  408.  
  409.     def zoom_factor(self):
  410.         return self._webengineviews[0].zoomFactor() if self._webengineviews else 1.0
  411.  
  412.     def set_zoom_factor(self, z):
  413.         for w in self._webengineviews:
  414.             w.setZoomFactor(z)
  415.  
  416.     def _handle_tab_context_menu(self, point):
  417.         index = self.tabBar().tabAt(point)
  418.         if index < 0:
  419.             return
  420.         tab_count = len(self._webengineviews)
  421.         context_menu = QMenu()
  422.         duplicate_tab_action = context_menu.addAction("Duplicate Tab")
  423.         close_other_tabs_action = context_menu.addAction("Close Other Tabs")
  424.         close_other_tabs_action.setEnabled(tab_count > 1)
  425.         close_tabs_to_the_right_action = context_menu.addAction("Close Tabs to the Right")
  426.         close_tabs_to_the_right_action.setEnabled(index < tab_count - 1)
  427.         close_tab_action = context_menu.addAction("&Close Tab")
  428.         chosen_action = context_menu.exec_(self.tabBar().mapToGlobal(point))
  429.         if chosen_action == duplicate_tab_action:
  430.             current_url = self.url()
  431.             self.add_browser_tab().load(current_url)
  432.         elif chosen_action == close_other_tabs_action:
  433.             for t in range(tab_count - 1, -1, -1):
  434.                 if t != index:
  435.                      self.handle_tab_close_request(t)
  436.         elif chosen_action == close_tabs_to_the_right_action:
  437.             for t in range(tab_count - 1, index, -1):
  438.                 self.handle_tab_close_request(t)
  439.         elif chosen_action == close_tab_action:
  440.             self.handle_tab_close_request(index)
  441.  
  442.     def handle_tab_close_request(self, index):
  443.         if (index >= 0 and self.count() > 1):
  444.             self._webengineviews.remove(self._webengineviews[index])
  445.             self.removeTab(index)
  446.  
  447.     def close_current_tab(self):
  448.         self.handle_tab_close_request(self.currentIndex())
  449.  
  450.     def _trigger_action(self, action):
  451.         index = self.currentIndex()
  452.         if index >= 0:
  453.             self._webengineviews[index].page().triggerAction(action)
  454.  
  455.     def _index_of_page(self, web_page):
  456.         for p in range(0, len(self._webengineviews)):
  457.             if (self._webengineviews[p].page() == web_page):
  458.                 return p
  459.         return -1
  460.  
  461.     def _download_requested(self, item):
  462.         self.downloadRequested.emit(item)
  463.  
  464.  
  465.  
  466.  
  467.  
  468.  
  469. #############################################################################
  470. ##
  471. ## Copyright (C) 2018 The Qt Company Ltd.
  472.  
  473. #############################################################################
  474.  
  475. import sys
  476. from PySide2 import QtCore
  477. from PySide2.QtCore import QDir, QFileInfo, QStandardPaths, Qt, QUrl
  478. from PySide2.QtGui import QDesktopServices
  479. from PySide2.QtWidgets import (QAction, QLabel, QMenu, QProgressBar,
  480.     QStyleFactory, QWidget)
  481. from PySide2.QtWebEngineWidgets import QWebEngineDownloadItem
  482.  
  483. # A QProgressBar with context menu for displaying downloads in a QStatusBar.
  484. class DownloadWidget(QProgressBar):
  485.     """Lets you track progress of a QWebEngineDownloadItem."""
  486.     finished = QtCore.Signal()
  487.     remove_requested = QtCore.Signal()
  488.  
  489.     def __init__(self, download_item):
  490.         super(DownloadWidget, self).__init__()
  491.         self._download_item = download_item
  492.         download_item.finished.connect(self._finished)
  493.         download_item.downloadProgress.connect(self._download_progress)
  494.         download_item.stateChanged.connect(self._update_tool_tip())
  495.         path = download_item.path()
  496.         self.setMaximumWidth(300)
  497.         # Shorten 'PySide2-5.11.0a1-5.11.0-cp36-cp36m-linux_x86_64.whl'...
  498.         description = QFileInfo(path).fileName()
  499.         description_length = len(description)
  500.         if description_length > 30:
  501.             description = '{}...{}'.format(description[0:10], description[description_length - 10:])
  502.         self.setFormat('{} %p%'.format(description))
  503.         self.setOrientation(Qt.Horizontal)
  504.         self.setMinimum(0)
  505.         self.setValue(0)
  506.         self.setMaximum(100)
  507.         self._update_tool_tip()
  508.         # Force progress bar text to be shown on macoS by using 'fusion' style
  509.         if sys.platform == 'darwin':
  510.             self.setStyle(QStyleFactory.create('fusion'))
  511.  
  512.     @staticmethod
  513.     def open_file(file):
  514.         QDesktopServices.openUrl(QUrl.fromLocalFile(file))
  515.  
  516.     @staticmethod
  517.     def open_download_directory():
  518.         path = QStandardPaths.writableLocation(QStandardPaths.DownloadLocation)
  519.         DownloadWidget.open_file(path)
  520.  
  521.     def state(self):
  522.         return self._download_item.state()
  523.  
  524.     def _update_tool_tip(self):
  525.         path = self._download_item.path()
  526.         tool_tip = "{}\n{}".format(self._download_item.url().toString(),
  527.             QDir.toNativeSeparators(path))
  528.         total_bytes = self._download_item.total_bytes()
  529.         if total_bytes > 0:
  530.             tool_tip += "\n{}K".format(total_bytes / 1024)
  531.         state = self.state()
  532.         if state == QWebEngineDownloadItem.DownloadRequested:
  533.             tool_tip += "\n(requested)"
  534.         elif state == QWebEngineDownloadItem.DownloadInProgress:
  535.             tool_tip += "\n(downloading)"
  536.         elif state == QWebEngineDownloadItem.DownloadCompleted:
  537.             tool_tip += "\n(completed)"
  538.         elif state == QWebEngineDownloadItem.DownloadCancelled:
  539.             tool_tip += "\n(cancelled)"
  540.         else:
  541.             tool_tip += "\n(interrupted)"
  542.         self.setToolTip(tool_tip)
  543.  
  544.     def _download_progress(self, bytes_received, bytes_total):
  545.         self.setValue(int(100 * bytes_received / bytes_total))
  546.  
  547.     def _finished(self):
  548.         self._update_tool_tip()
  549.         self.finished.emit()
  550.  
  551.     def _launch(self):
  552.         DownloadWidget.open_file(self._download_item.path())
  553.  
  554.     def mouse_double_click_event(self, event):
  555.         if self.state() == QWebEngineDownloadItem.DownloadCompleted:
  556.             self._launch()
  557.  
  558.     def context_menu_event(self, event):
  559.         state = self.state()
  560.         context_menu = QMenu()
  561.         launch_action = context_menu.addAction("Launch")
  562.         launch_action.setEnabled(state == QWebEngineDownloadItem.DownloadCompleted)
  563.         show_in_folder_action = context_menu.addAction("Show in Folder")
  564.         show_in_folder_action.setEnabled(state == QWebEngineDownloadItem.DownloadCompleted)
  565.         cancel_action = context_menu.addAction("Cancel")
  566.         cancel_action.setEnabled(state == QWebEngineDownloadItem.DownloadInProgress)
  567.         remove_action = context_menu.addAction("Remove")
  568.         remove_action.setEnabled(state != QWebEngineDownloadItem.DownloadInProgress)
  569.  
  570.         chosen_action = context_menu.exec_(event.globalPos())
  571.         if chosen_action == launch_action:
  572.             self._launch()
  573.         elif chosen_action == show_in_folder_action:
  574.             DownloadWidget.open_file(QFileInfo(self._download_item.path()).absolutePath())
  575.         elif chosen_action == cancel_action:
  576.             self._download_item.cancel()
  577.         elif chosen_action == remove_action:
  578.             self.remove_requested.emit()
  579.  
  580.  
  581.  
  582.  
  583.  
  584. #############################################################################
  585. ##
  586. ## Copyright (C) 2018 The Qt Company Ltd.
  587.  
  588. ##
  589. #############################################################################
  590.  
  591. from PySide2 import QtCore
  592. from PySide2.QtCore import Qt, QUrl
  593. from PySide2.QtGui import QIcon, QKeySequence
  594. from PySide2.QtWidgets import (QAction, QCheckBox, QDockWidget, QHBoxLayout,
  595.     QLabel, QLineEdit, QToolBar, QToolButton, QWidget)
  596. from PySide2.QtWebEngineWidgets import QWebEnginePage
  597.  
  598. # A Find tool bar (bottom area)
  599. class FindToolBar(QToolBar):
  600.  
  601.     find = QtCore.Signal(str, QWebEnginePage.FindFlags)
  602.  
  603.     def __init__(self):
  604.         super(FindToolBar, self).__init__()
  605.         self._line_edit = QLineEdit()
  606.         self._line_edit.setClearButtonEnabled(True)
  607.         self._line_edit.setPlaceholderText("Find...")
  608.         self._line_edit.setMaximumWidth(300)
  609.         self._line_edit.returnPressed.connect(self._find_next)
  610.         self.addWidget(self._line_edit)
  611.  
  612.         self._previous_button = QToolButton()
  613.         self._previous_button.setIcon(QIcon(':/qt-project.org/styles/commonstyle/images/up-32.png'))
  614.         self._previous_button.clicked.connect(self._find_previous)
  615.         self.addWidget(self._previous_button)
  616.  
  617.         self._next_button = QToolButton()
  618.         self._next_button.setIcon(QIcon(':/qt-project.org/styles/commonstyle/images/down-32.png'))
  619.         self._next_button.clicked.connect(self._find_next)
  620.         self.addWidget(self._next_button)
  621.  
  622.         self._case_sensitive_checkbox = QCheckBox('Case Sensitive')
  623.         self.addWidget(self._case_sensitive_checkbox)
  624.  
  625.         self._hideButton = QToolButton()
  626.         self._hideButton.setShortcut(QKeySequence(Qt.Key_Escape))
  627.         self._hideButton.setIcon(QIcon(':/qt-project.org/styles/macstyle/images/closedock-16.png'))
  628.         self._hideButton.clicked.connect(self.hide)
  629.         self.addWidget(self._hideButton)
  630.  
  631.     def focus_find(self):
  632.         self._line_edit.setFocus()
  633.  
  634.     def _emit_find(self, backward):
  635.         needle =  self._line_edit.text().strip()
  636.         if needle:
  637.             flags = QWebEnginePage.FindFlags()
  638.             if self._case_sensitive_checkbox.isChecked():
  639.                 flags |= QWebEnginePage.FindCaseSensitively
  640.             if backward:
  641.                 flags |= QWebEnginePage.FindBackward
  642.             self.find.emit(needle, flags)
  643.  
  644.     def _find_next(self):
  645.         self._emit_find(False)
  646.  
  647.     def _find_previous(self):
  648.         self._emit_find(True)
  649.  
  650.  
  651.  
  652.  
  653.  
  654.  
  655. #############################################################################
  656. ##
  657. ## Copyright (C) 2018 The Qt Company Ltd.
  658.  
  659. ##
  660. #############################################################################
  661.  
  662. """PySide2 WebEngineWidgets Example"""
  663.  
  664. import sys
  665. from bookmarkwidget import BookmarkWidget
  666. from browsertabwidget import BrowserTabWidget
  667. from downloadwidget import DownloadWidget
  668. from findtoolbar import FindToolBar
  669. from webengineview import QWebEnginePage, WebEngineView
  670. from PySide2 import QtCore
  671. from PySide2.QtCore import Qt, QUrl
  672. from PySide2.QtGui import QCloseEvent, QKeySequence, QIcon
  673. from PySide2.QtWidgets import (qApp, QAction, QApplication, QDesktopWidget,
  674.     QDockWidget, QLabel, QLineEdit, QMainWindow, QMenu, QMenuBar, QPushButton,
  675.     QStatusBar, QToolBar)
  676. from PySide2.QtWebEngineWidgets import (QWebEngineDownloadItem, QWebEnginePage,
  677.     QWebEngineView)
  678.  
  679. main_windows = []
  680.  
  681. def create_main_window():
  682.     """Creates a MainWindow using 75% of the available screen resolution."""
  683.     main_win = MainWindow()
  684.     main_windows.append(main_win)
  685.     available_geometry = app.desktop().availableGeometry(main_win)
  686.     main_win.resize(available_geometry.width() * 2 / 3, available_geometry.height() * 2 / 3)
  687.     main_win.show()
  688.     return main_win
  689.  
  690. def create_main_window_with_browser():
  691.     """Creates a MainWindow with a BrowserTabWidget."""
  692.     main_win = create_main_window()
  693.     return main_win.add_browser_tab()
  694.  
  695. class MainWindow(QMainWindow):
  696.     """Provides the parent window that includes the BookmarkWidget,
  697.    BrowserTabWidget, and a DownloadWidget, to offer the complete
  698.    web browsing experience."""
  699.     def __init__(self):
  700.         super(MainWindow, self).__init__()
  701.  
  702.         self.setWindowTitle('PySide2 tabbed browser Example')
  703.  
  704.         self._tab_widget = BrowserTabWidget(create_main_window_with_browser)
  705.         self._tab_widget.enabled_changed.connect(self._enabled_changed)
  706.         self._tab_widget.download_requested.connect(self._download_requested)
  707.         self.setCentralWidget(self._tab_widget)
  708.         self.connect(self._tab_widget, QtCore.SIGNAL("url_changed(QUrl)"),
  709.                      self.url_changed)
  710.  
  711.         self._bookmark_dock = QDockWidget()
  712.         self._bookmark_dock.setWindowTitle('Bookmarks')
  713.         self._bookmark_widget = BookmarkWidget()
  714.         self._bookmark_widget.open_bookmark.connect(self.load_url)
  715.         self._bookmark_widget.open_bookmark_in_new_tab.connect(self.load_url_in_new_tab)
  716.         self._bookmark_dock.setWidget(self._bookmark_widget)
  717.         self.addDockWidget(Qt.LeftDockWidgetArea, self._bookmark_dock)
  718.  
  719.         self._find_tool_bar = None
  720.  
  721.         self._actions = {}
  722.         self._create_menu()
  723.  
  724.         self._tool_bar = QToolBar()
  725.         self.addToolBar(self._tool_bar)
  726.         for action in self._actions.values():
  727.             if not action.icon().isNull():
  728.                 self._tool_bar.addAction(action)
  729.  
  730.         self._addres_line_edit = QLineEdit()
  731.         self._addres_line_edit.setClearButtonEnabled(True)
  732.         self._addres_line_edit.returnPressed.connect(self.load)
  733.         self._tool_bar.addWidget(self._addres_line_edit)
  734.         self._zoom_label = QLabel()
  735.         self.statusBar().addPermanentWidget(self._zoom_label)
  736.         self._update_zoom_label()
  737.  
  738.         self._bookmarksToolBar = QToolBar()
  739.         self.addToolBar(Qt.TopToolBarArea, self._bookmarksToolBar)
  740.         self.insertToolBarBreak(self._bookmarksToolBar)
  741.         self._bookmark_widget.changed.connect(self._update_bookmarks)
  742.         self._update_bookmarks()
  743.  
  744.     def _update_bookmarks(self):
  745.         self._bookmark_widget.populate_tool_bar(self._bookmarksToolBar)
  746.         self._bookmark_widget.populate_other(self._bookmark_menu, 3)
  747.  
  748.     def _create_menu(self):
  749.         file_menu = self.menuBar().addMenu("&File")
  750.         exit_action = QAction(QIcon.fromTheme("application-exit"), "E&xit",
  751.                              self, shortcut = "Ctrl+Q", triggered=qApp.quit)
  752.         file_menu.addAction(exit_action)
  753.  
  754.         navigation_menu = self.menuBar().addMenu("&Navigation")
  755.  
  756.         style_icons = ':/qt-project.org/styles/commonstyle/images/'
  757.         back_action = QAction(QIcon.fromTheme("go-previous",
  758.                                              QIcon(style_icons + 'left-32.png')),
  759.                              "Back", self,
  760.                              shortcut = QKeySequence(QKeySequence.Back),
  761.                              triggered = self._tab_widget.back)
  762.         self._actions[QWebEnginePage.Back] = back_action
  763.         back_action.setEnabled(False)
  764.         navigation_menu.addAction(back_action)
  765.         forward_action = QAction(QIcon.fromTheme("go-next",
  766.                                                 QIcon(style_icons + 'right-32.png')),
  767.                                 "Forward", self,
  768.                                 shortcut = QKeySequence(QKeySequence.Forward),
  769.                                 triggered = self._tab_widget.forward)
  770.         forward_action.setEnabled(False)
  771.         self._actions[QWebEnginePage.Forward] = forward_action
  772.  
  773.         navigation_menu.addAction(forward_action)
  774.         reload_action = QAction(QIcon(style_icons + 'refresh-32.png'),
  775.                                "Reload", self,
  776.                                shortcut = QKeySequence(QKeySequence.Refresh),
  777.                                triggered = self._tab_widget.reload)
  778.         self._actions[QWebEnginePage.Reload] = reload_action
  779.         reload_action.setEnabled(False)
  780.         navigation_menu.addAction(reload_action)
  781.  
  782.         navigation_menu.addSeparator()
  783.  
  784.         new_tab_action = QAction("New Tab", self,
  785.                              shortcut = 'Ctrl+T',
  786.                              triggered = self.add_browser_tab)
  787.         navigation_menu.addAction(new_tab_action)
  788.  
  789.         close_tab_action = QAction("Close Current Tab", self,
  790.                                  shortcut = "Ctrl+W",
  791.                                  triggered = self._close_current_tab)
  792.         navigation_menu.addAction(close_tab_action)
  793.  
  794.         edit_menu = self.menuBar().addMenu("&Edit")
  795.  
  796.         find_action = QAction("Find", self,
  797.                              shortcut = QKeySequence(QKeySequence.Find),
  798.                              triggered = self._show_find)
  799.         edit_menu.addAction(find_action)
  800.  
  801.         edit_menu.addSeparator()
  802.         undo_action = QAction("Undo", self,
  803.                              shortcut = QKeySequence(QKeySequence.Undo),
  804.                              triggered = self._tab_widget.undo)
  805.         self._actions[QWebEnginePage.Undo] = undo_action
  806.         undo_action.setEnabled(False)
  807.         edit_menu.addAction(undo_action)
  808.  
  809.         redo_action = QAction("Redo", self,
  810.                              shortcut = QKeySequence(QKeySequence.Redo),
  811.                              triggered = self._tab_widget.redo)
  812.         self._actions[QWebEnginePage.Redo] = redo_action
  813.         redo_action.setEnabled(False)
  814.         edit_menu.addAction(redo_action)
  815.  
  816.         edit_menu.addSeparator()
  817.  
  818.         cut_action = QAction("Cut", self,
  819.                             shortcut = QKeySequence(QKeySequence.Cut),
  820.                             triggered = self._tab_widget.cut)
  821.         self._actions[QWebEnginePage.Cut] = cut_action
  822.         cut_action.setEnabled(False)
  823.         edit_menu.addAction(cut_action)
  824.  
  825.         copy_action = QAction("Copy", self,
  826.                              shortcut = QKeySequence(QKeySequence.Copy),
  827.                              triggered = self._tab_widget.copy)
  828.         self._actions[QWebEnginePage.Copy] = copy_action
  829.         copy_action.setEnabled(False)
  830.         edit_menu.addAction(copy_action)
  831.  
  832.         paste_action = QAction("Paste", self,
  833.                              shortcut = QKeySequence(QKeySequence.Paste),
  834.                              triggered = self._tab_widget.paste)
  835.         self._actions[QWebEnginePage.Paste] = paste_action
  836.         paste_action.setEnabled(False)
  837.         edit_menu.addAction(paste_action)
  838.  
  839.         edit_menu.addSeparator()
  840.  
  841.         select_all_action = QAction("Select All", self,
  842.                                   shortcut = QKeySequence(QKeySequence.SelectAll),
  843.                                   triggered = self._tab_widget.select_all)
  844.         self._actions[QWebEnginePage.SelectAll] = select_all_action
  845.         select_all_action.setEnabled(False)
  846.         edit_menu.addAction(select_all_action)
  847.  
  848.         self._bookmark_menu = self.menuBar().addMenu("&Bookmarks")
  849.         add_bookmark_action = QAction("&Add Bookmark", self,
  850.                                     triggered = self._add_bookmark)
  851.         self._bookmark_menu.addAction(add_bookmark_action)
  852.         add_tool_bar_bookmark_action = QAction("&Add Bookmark to Tool Bar", self,
  853.                                            triggered = self._add_tool_bar_bookmark)
  854.         self._bookmark_menu.addAction(add_tool_bar_bookmark_action)
  855.         self._bookmark_menu.addSeparator()
  856.  
  857.         tools_menu = self.menuBar().addMenu("&Tools")
  858.         download_action = QAction("Open Downloads", self,
  859.                                  triggered = DownloadWidget.open_download_directory)
  860.         tools_menu.addAction(download_action)
  861.  
  862.         window_menu = self.menuBar().addMenu("&Window")
  863.  
  864.         window_menu.addAction(self._bookmark_dock.toggleViewAction())
  865.  
  866.         window_menu.addSeparator()
  867.  
  868.         zoom_in_action = QAction(QIcon.fromTheme("zoom-in"),
  869.                                "Zoom In", self,
  870.                                shortcut = QKeySequence(QKeySequence.ZoomIn),
  871.                                triggered = self._zoom_in)
  872.         window_menu.addAction(zoom_in_action)
  873.         zoom_out_action = QAction(QIcon.fromTheme("zoom-out"),
  874.                                 "Zoom Out", self,
  875.                                 shortcut = QKeySequence(QKeySequence.ZoomOut),
  876.                                 triggered = self._zoom_out)
  877.         window_menu.addAction(zoom_out_action)
  878.  
  879.         reset_zoom_action = QAction(QIcon.fromTheme("zoom-original"),
  880.                                   "Reset Zoom", self,
  881.                                   shortcut = "Ctrl+0",
  882.                                   triggered = self._reset_zoom)
  883.         window_menu.addAction(reset_zoom_action)
  884.  
  885.         about_menu = self.menuBar().addMenu("&About")
  886.         about_action = QAction("About Qt", self,
  887.                               shortcut = QKeySequence(QKeySequence.HelpContents),
  888.                               triggered=qApp.aboutQt)
  889.         about_menu.addAction(about_action)
  890.  
  891.     def add_browser_tab(self):
  892.         return self._tab_widget.add_browser_tab()
  893.  
  894.     def _close_current_tab(self):
  895.         if self._tab_widget.count() > 1:
  896.             self._tab_widget.close_current_tab()
  897.         else:
  898.             self.close()
  899.  
  900.     def close_event(self, event):
  901.         main_windows.remove(self)
  902.         event.accept()
  903.  
  904.     def load(self):
  905.         url_string = self._addres_line_edit.text().strip()
  906.         if url_string:
  907.             self.load_url_string(url_string)
  908.  
  909.     def load_url_string(self, url_s):
  910.         url = QUrl.fromUserInput(url_s)
  911.         if (url.isValid()):
  912.             self.load_url(url)
  913.  
  914.     def load_url(self, url):
  915.         self._tab_widget.load(url)
  916.  
  917.     def load_url_in_new_tab(self, url):
  918.         self.add_browser_tab().load(url)
  919.  
  920.     def url_changed(self, url):
  921.         self._addres_line_edit.setText(url.toString())
  922.  
  923.     def _enabled_changed(self, web_action, enabled):
  924.         action = self._actions[web_action]
  925.         if action:
  926.             action.setEnabled(enabled)
  927.  
  928.     def _add_bookmark(self):
  929.         index = self._tab_widget.currentIndex()
  930.         if index >= 0:
  931.             url = self._tab_widget.url()
  932.             title = self._tab_widget.tabText(index)
  933.             icon = self._tab_widget.tabIcon(index)
  934.             self._bookmark_widget.add_bookmark(url, title, icon)
  935.  
  936.     def _add_tool_bar_bookmark(self):
  937.         index = self._tab_widget.currentIndex()
  938.         if index >= 0:
  939.             url = self._tab_widget.url()
  940.             title = self._tab_widget.tabText(index)
  941.             icon = self._tab_widget.tabIcon(index)
  942.             self._bookmark_widget.add_tool_bar_bookmark(url, title, icon)
  943.  
  944.     def _zoom_in(self):
  945.         new_zoom = self._tab_widget.zoom_factor() * 1.5
  946.         if (new_zoom <= WebEngineView.maximum_zoom_factor()):
  947.             self._tab_widget.set_zoom_factor(new_zoom)
  948.             self._update_zoom_label()
  949.  
  950.     def _zoom_out(self):
  951.         new_zoom = self._tab_widget.zoom_factor() / 1.5
  952.         if (new_zoom >= WebEngineView.minimum_zoom_factor()):
  953.             self._tab_widget.set_zoom_factor(new_zoom)
  954.             self._update_zoom_label()
  955.  
  956.     def _reset_zoom(self):
  957.         self._tab_widget.set_zoom_factor(1)
  958.         self._update_zoom_label()
  959.  
  960.     def _update_zoom_label(self):
  961.         percent = int(self._tab_widget.zoom_factor() * 100)
  962.         self._zoom_label.setText("{}%".format(percent))
  963.  
  964.     def _download_requested(self, item):
  965.         # Remove old downloads before opening a new one
  966.         for old_download in self.statusBar().children():
  967.             if type(old_download).__name__ == 'download_widget' and \
  968.                 old_download.state() != QWebEngineDownloadItem.DownloadInProgress:
  969.                 self.statusBar().removeWidget(old_download)
  970.                 del old_download
  971.  
  972.         item.accept()
  973.         download_widget = download_widget(item)
  974.         download_widget.removeRequested.connect(self._remove_download_requested,
  975.                                                Qt.QueuedConnection)
  976.         self.statusBar().addWidget(download_widget)
  977.  
  978.     def _remove_download_requested(self):
  979.             download_widget = self.sender()
  980.             self.statusBar().removeWidget(download_widget)
  981.             del download_widget
  982.  
  983.     def _show_find(self):
  984.         if self._find_tool_bar is None:
  985.             self._find_tool_bar = FindToolBar()
  986.             self._find_tool_bar.find.connect(self._tab_widget.find)
  987.             self.addToolBar(Qt.BottomToolBarArea, self._find_tool_bar)
  988.         else:
  989.             self._find_tool_bar.show()
  990.         self._find_tool_bar.focus_find()
  991.  
  992.     def write_bookmarks(self):
  993.         self._bookmark_widget.write_bookmarks()
  994.  
  995. if __name__ == '__main__':
  996.     app = QApplication(sys.argv)
  997.     main_win = create_main_window()
  998.     initial_urls = sys.argv[1:]
  999.     if not initial_urls:
  1000.         initial_urls.append('http://qt.io')
  1001.     for url in initial_urls:
  1002.         main_win.load_url_in_new_tab(QUrl.fromUserInput(url))
  1003.     exit_code = app.exec_()
  1004.     main_win.write_bookmarks()
  1005.     sys.exit(exit_code)
  1006.  
  1007.  
  1008.  
  1009.  
  1010.  
  1011. #############################################################################
  1012.  
  1013.  
  1014. #############################################################################
  1015.  
  1016. import sys
  1017. from PySide2.QtWebEngineWidgets import QWebEnginePage, QWebEngineView
  1018.  
  1019. from PySide2 import QtCore
  1020.  
  1021. _web_actions = [QWebEnginePage.Back, QWebEnginePage.Forward,
  1022.                QWebEnginePage.Reload,
  1023.                QWebEnginePage.Undo, QWebEnginePage.Redo,
  1024.                QWebEnginePage.Cut, QWebEnginePage.Copy,
  1025.                QWebEnginePage.Paste, QWebEnginePage.SelectAll]
  1026.  
  1027. class WebEngineView(QWebEngineView):
  1028.  
  1029.     enabled_changed = QtCore.Signal(QWebEnginePage.WebAction, bool)
  1030.  
  1031.     @staticmethod
  1032.     def web_actions():
  1033.         return _web_actions
  1034.  
  1035.     @staticmethod
  1036.     def minimum_zoom_factor():
  1037.         return 0.25
  1038.  
  1039.     @staticmethod
  1040.     def maximum_zoom_factor():
  1041.         return 5
  1042.  
  1043.     def __init__(self, tab_factory_func, window_factory_func):
  1044.         super(WebEngineView, self).__init__()
  1045.         self._tab_factory_func = tab_factory_func
  1046.         self._window_factory_func = window_factory_func
  1047.         page = self.page()
  1048.         self._actions = {}
  1049.         for web_action in WebEngineView.web_actions():
  1050.             action = page.action(web_action)
  1051.             action.changed.connect(self._enabled_changed)
  1052.             self._actions[action] = web_action
  1053.  
  1054.     def is_web_action_enabled(self, web_action):
  1055.         return self.page().action(web_action).isEnabled()
  1056.  
  1057.     def create_window(self, window_type):
  1058.         if window_type == QWebEnginePage.WebBrowserTab or window_type == QWebEnginePage.WebBrowserBackgroundTab:
  1059.             return self._tab_factory_func()
  1060.         return self._window_factory_func()
  1061.  
  1062.     def _enabled_changed(self):
  1063.         action = self.sender()
  1064.         web_action = self._actions[action]
  1065.         self.enabled_changed.emit(web_action, action.isEnabled())
  1066.  
  1067.  
  1068.  
  1069.  
  1070. #tabbed browser.pyqtc
  1071. main.py
  1072. bookmarkwidget.py
  1073. browsertabwidget.py
  1074. downloadwidget.py
  1075. findtoolbar.py
  1076. webengineview.py
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement