1 2############################################################################# 3## 4## Copyright (C) 2018 The Qt Company Ltd. 5## Contact: http://www.qt.io/licensing/ 6## 7## This file is part of the Qt for Python examples of the Qt Toolkit. 8## 9## $QT_BEGIN_LICENSE:BSD$ 10## You may use this file under the terms of the BSD license as follows: 11## 12## "Redistribution and use in source and binary forms, with or without 13## modification, are permitted provided that the following conditions are 14## met: 15## * Redistributions of source code must retain the above copyright 16## notice, this list of conditions and the following disclaimer. 17## * Redistributions in binary form must reproduce the above copyright 18## notice, this list of conditions and the following disclaimer in 19## the documentation and/or other materials provided with the 20## distribution. 21## * Neither the name of The Qt Company Ltd nor the names of its 22## contributors may be used to endorse or promote products derived 23## from this software without specific prior written permission. 24## 25## 26## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 30## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 32## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 36## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 37## 38## $QT_END_LICENSE$ 39## 40############################################################################# 41 42"""PySide2 WebEngineWidgets Example""" 43 44import sys 45from bookmarkwidget import BookmarkWidget 46from browsertabwidget import BrowserTabWidget 47from downloadwidget import DownloadWidget 48from findtoolbar import FindToolBar 49from webengineview import WebEngineView 50from PySide2 import QtCore 51from PySide2.QtCore import Qt, QUrl 52from PySide2.QtGui import QKeySequence, QIcon 53from PySide2.QtWidgets import (QAction, QApplication, QDockWidget, QLabel, 54 QLineEdit, QMainWindow, QToolBar) 55from PySide2.QtWebEngineWidgets import QWebEngineDownloadItem, QWebEnginePage 56 57main_windows = [] 58 59 60def create_main_window(): 61 """Creates a MainWindow using 75% of the available screen resolution.""" 62 main_win = MainWindow() 63 main_windows.append(main_win) 64 available_geometry = app.desktop().availableGeometry(main_win) 65 main_win.resize(available_geometry.width() * 2 / 3, 66 available_geometry.height() * 2 / 3) 67 main_win.show() 68 return main_win 69 70 71def create_main_window_with_browser(): 72 """Creates a MainWindow with a BrowserTabWidget.""" 73 main_win = create_main_window() 74 return main_win.add_browser_tab() 75 76 77class MainWindow(QMainWindow): 78 """Provides the parent window that includes the BookmarkWidget, 79 BrowserTabWidget, and a DownloadWidget, to offer the complete 80 web browsing experience.""" 81 82 def __init__(self): 83 super(MainWindow, self).__init__() 84 85 self.setWindowTitle('PySide2 tabbed browser Example') 86 87 self._tab_widget = BrowserTabWidget(create_main_window_with_browser) 88 self._tab_widget.enabled_changed.connect(self._enabled_changed) 89 self._tab_widget.download_requested.connect(self._download_requested) 90 self.setCentralWidget(self._tab_widget) 91 self.connect(self._tab_widget, QtCore.SIGNAL("url_changed(QUrl)"), 92 self.url_changed) 93 94 self._bookmark_dock = QDockWidget() 95 self._bookmark_dock.setWindowTitle('Bookmarks') 96 self._bookmark_widget = BookmarkWidget() 97 self._bookmark_widget.open_bookmark.connect(self.load_url) 98 self._bookmark_widget.open_bookmark_in_new_tab.connect(self.load_url_in_new_tab) 99 self._bookmark_dock.setWidget(self._bookmark_widget) 100 self.addDockWidget(Qt.LeftDockWidgetArea, self._bookmark_dock) 101 102 self._find_tool_bar = None 103 104 self._actions = {} 105 self._create_menu() 106 107 self._tool_bar = QToolBar() 108 self.addToolBar(self._tool_bar) 109 for action in self._actions.values(): 110 if not action.icon().isNull(): 111 self._tool_bar.addAction(action) 112 113 self._addres_line_edit = QLineEdit() 114 self._addres_line_edit.setClearButtonEnabled(True) 115 self._addres_line_edit.returnPressed.connect(self.load) 116 self._tool_bar.addWidget(self._addres_line_edit) 117 self._zoom_label = QLabel() 118 self.statusBar().addPermanentWidget(self._zoom_label) 119 self._update_zoom_label() 120 121 self._bookmarksToolBar = QToolBar() 122 self.addToolBar(Qt.TopToolBarArea, self._bookmarksToolBar) 123 self.insertToolBarBreak(self._bookmarksToolBar) 124 self._bookmark_widget.changed.connect(self._update_bookmarks) 125 self._update_bookmarks() 126 127 def _update_bookmarks(self): 128 self._bookmark_widget.populate_tool_bar(self._bookmarksToolBar) 129 self._bookmark_widget.populate_other(self._bookmark_menu, 3) 130 131 def _create_menu(self): 132 file_menu = self.menuBar().addMenu("&File") 133 exit_action = QAction(QIcon.fromTheme("application-exit"), "E&xit", 134 self, shortcut="Ctrl+Q", triggered=qApp.quit) 135 file_menu.addAction(exit_action) 136 137 navigation_menu = self.menuBar().addMenu("&Navigation") 138 139 style_icons = ':/qt-project.org/styles/commonstyle/images/' 140 back_action = QAction(QIcon.fromTheme("go-previous", 141 QIcon(style_icons + 'left-32.png')), 142 "Back", self, 143 shortcut=QKeySequence(QKeySequence.Back), 144 triggered=self._tab_widget.back) 145 self._actions[QWebEnginePage.Back] = back_action 146 back_action.setEnabled(False) 147 navigation_menu.addAction(back_action) 148 forward_action = QAction(QIcon.fromTheme("go-next", 149 QIcon(style_icons + 'right-32.png')), 150 "Forward", self, 151 shortcut=QKeySequence(QKeySequence.Forward), 152 triggered=self._tab_widget.forward) 153 forward_action.setEnabled(False) 154 self._actions[QWebEnginePage.Forward] = forward_action 155 156 navigation_menu.addAction(forward_action) 157 reload_action = QAction(QIcon(style_icons + 'refresh-32.png'), 158 "Reload", self, 159 shortcut=QKeySequence(QKeySequence.Refresh), 160 triggered=self._tab_widget.reload) 161 self._actions[QWebEnginePage.Reload] = reload_action 162 reload_action.setEnabled(False) 163 navigation_menu.addAction(reload_action) 164 165 navigation_menu.addSeparator() 166 167 new_tab_action = QAction("New Tab", self, 168 shortcut='Ctrl+T', 169 triggered=self.add_browser_tab) 170 navigation_menu.addAction(new_tab_action) 171 172 close_tab_action = QAction("Close Current Tab", self, 173 shortcut="Ctrl+W", 174 triggered=self._close_current_tab) 175 navigation_menu.addAction(close_tab_action) 176 177 navigation_menu.addSeparator() 178 179 history_action = QAction("History...", self, 180 triggered=self._tab_widget.show_history) 181 navigation_menu.addAction(history_action) 182 183 edit_menu = self.menuBar().addMenu("&Edit") 184 185 find_action = QAction("Find", self, 186 shortcut=QKeySequence(QKeySequence.Find), 187 triggered=self._show_find) 188 edit_menu.addAction(find_action) 189 190 edit_menu.addSeparator() 191 undo_action = QAction("Undo", self, 192 shortcut=QKeySequence(QKeySequence.Undo), 193 triggered=self._tab_widget.undo) 194 self._actions[QWebEnginePage.Undo] = undo_action 195 undo_action.setEnabled(False) 196 edit_menu.addAction(undo_action) 197 198 redo_action = QAction("Redo", self, 199 shortcut=QKeySequence(QKeySequence.Redo), 200 triggered=self._tab_widget.redo) 201 self._actions[QWebEnginePage.Redo] = redo_action 202 redo_action.setEnabled(False) 203 edit_menu.addAction(redo_action) 204 205 edit_menu.addSeparator() 206 207 cut_action = QAction("Cut", self, 208 shortcut=QKeySequence(QKeySequence.Cut), 209 triggered=self._tab_widget.cut) 210 self._actions[QWebEnginePage.Cut] = cut_action 211 cut_action.setEnabled(False) 212 edit_menu.addAction(cut_action) 213 214 copy_action = QAction("Copy", self, 215 shortcut=QKeySequence(QKeySequence.Copy), 216 triggered=self._tab_widget.copy) 217 self._actions[QWebEnginePage.Copy] = copy_action 218 copy_action.setEnabled(False) 219 edit_menu.addAction(copy_action) 220 221 paste_action = QAction("Paste", self, 222 shortcut=QKeySequence(QKeySequence.Paste), 223 triggered=self._tab_widget.paste) 224 self._actions[QWebEnginePage.Paste] = paste_action 225 paste_action.setEnabled(False) 226 edit_menu.addAction(paste_action) 227 228 edit_menu.addSeparator() 229 230 select_all_action = QAction("Select All", self, 231 shortcut=QKeySequence(QKeySequence.SelectAll), 232 triggered=self._tab_widget.select_all) 233 self._actions[QWebEnginePage.SelectAll] = select_all_action 234 select_all_action.setEnabled(False) 235 edit_menu.addAction(select_all_action) 236 237 self._bookmark_menu = self.menuBar().addMenu("&Bookmarks") 238 add_bookmark_action = QAction("&Add Bookmark", self, 239 triggered=self._add_bookmark) 240 self._bookmark_menu.addAction(add_bookmark_action) 241 add_tool_bar_bookmark_action = QAction("&Add Bookmark to Tool Bar", self, 242 triggered=self._add_tool_bar_bookmark) 243 self._bookmark_menu.addAction(add_tool_bar_bookmark_action) 244 self._bookmark_menu.addSeparator() 245 246 tools_menu = self.menuBar().addMenu("&Tools") 247 download_action = QAction("Open Downloads", self, 248 triggered=DownloadWidget.open_download_directory) 249 tools_menu.addAction(download_action) 250 251 window_menu = self.menuBar().addMenu("&Window") 252 253 window_menu.addAction(self._bookmark_dock.toggleViewAction()) 254 255 window_menu.addSeparator() 256 257 zoom_in_action = QAction(QIcon.fromTheme("zoom-in"), 258 "Zoom In", self, 259 shortcut=QKeySequence(QKeySequence.ZoomIn), 260 triggered=self._zoom_in) 261 window_menu.addAction(zoom_in_action) 262 zoom_out_action = QAction(QIcon.fromTheme("zoom-out"), 263 "Zoom Out", self, 264 shortcut=QKeySequence(QKeySequence.ZoomOut), 265 triggered=self._zoom_out) 266 window_menu.addAction(zoom_out_action) 267 268 reset_zoom_action = QAction(QIcon.fromTheme("zoom-original"), 269 "Reset Zoom", self, 270 shortcut="Ctrl+0", 271 triggered=self._reset_zoom) 272 window_menu.addAction(reset_zoom_action) 273 274 about_menu = self.menuBar().addMenu("&About") 275 about_action = QAction("About Qt", self, 276 shortcut=QKeySequence(QKeySequence.HelpContents), 277 triggered=qApp.aboutQt) 278 about_menu.addAction(about_action) 279 280 def add_browser_tab(self): 281 return self._tab_widget.add_browser_tab() 282 283 def _close_current_tab(self): 284 if self._tab_widget.count() > 1: 285 self._tab_widget.close_current_tab() 286 else: 287 self.close() 288 289 def close_event(self, event): 290 main_windows.remove(self) 291 event.accept() 292 293 def load(self): 294 url_string = self._addres_line_edit.text().strip() 295 if url_string: 296 self.load_url_string(url_string) 297 298 def load_url_string(self, url_s): 299 url = QUrl.fromUserInput(url_s) 300 if (url.isValid()): 301 self.load_url(url) 302 303 def load_url(self, url): 304 self._tab_widget.load(url) 305 306 def load_url_in_new_tab(self, url): 307 self.add_browser_tab().load(url) 308 309 def url_changed(self, url): 310 self._addres_line_edit.setText(url.toString()) 311 312 def _enabled_changed(self, web_action, enabled): 313 action = self._actions[web_action] 314 if action: 315 action.setEnabled(enabled) 316 317 def _add_bookmark(self): 318 index = self._tab_widget.currentIndex() 319 if index >= 0: 320 url = self._tab_widget.url() 321 title = self._tab_widget.tabText(index) 322 icon = self._tab_widget.tabIcon(index) 323 self._bookmark_widget.add_bookmark(url, title, icon) 324 325 def _add_tool_bar_bookmark(self): 326 index = self._tab_widget.currentIndex() 327 if index >= 0: 328 url = self._tab_widget.url() 329 title = self._tab_widget.tabText(index) 330 icon = self._tab_widget.tabIcon(index) 331 self._bookmark_widget.add_tool_bar_bookmark(url, title, icon) 332 333 def _zoom_in(self): 334 new_zoom = self._tab_widget.zoom_factor() * 1.5 335 if (new_zoom <= WebEngineView.maximum_zoom_factor()): 336 self._tab_widget.set_zoom_factor(new_zoom) 337 self._update_zoom_label() 338 339 def _zoom_out(self): 340 new_zoom = self._tab_widget.zoom_factor() / 1.5 341 if (new_zoom >= WebEngineView.minimum_zoom_factor()): 342 self._tab_widget.set_zoom_factor(new_zoom) 343 self._update_zoom_label() 344 345 def _reset_zoom(self): 346 self._tab_widget.set_zoom_factor(1) 347 self._update_zoom_label() 348 349 def _update_zoom_label(self): 350 percent = int(self._tab_widget.zoom_factor() * 100) 351 self._zoom_label.setText("{}%".format(percent)) 352 353 def _download_requested(self, item): 354 # Remove old downloads before opening a new one 355 for old_download in self.statusBar().children(): 356 if (type(old_download).__name__ == 'DownloadWidget' and 357 old_download.state() != QWebEngineDownloadItem.DownloadInProgress): 358 self.statusBar().removeWidget(old_download) 359 del old_download 360 361 item.accept() 362 download_widget = DownloadWidget(item) 363 download_widget.remove_requested.connect(self._remove_download_requested, 364 Qt.QueuedConnection) 365 self.statusBar().addWidget(download_widget) 366 367 def _remove_download_requested(self): 368 download_widget = self.sender() 369 self.statusBar().removeWidget(download_widget) 370 del download_widget 371 372 def _show_find(self): 373 if self._find_tool_bar is None: 374 self._find_tool_bar = FindToolBar() 375 self._find_tool_bar.find.connect(self._tab_widget.find) 376 self.addToolBar(Qt.BottomToolBarArea, self._find_tool_bar) 377 else: 378 self._find_tool_bar.show() 379 self._find_tool_bar.focus_find() 380 381 def write_bookmarks(self): 382 self._bookmark_widget.write_bookmarks() 383 384 385if __name__ == '__main__': 386 app = QApplication(sys.argv) 387 main_win = create_main_window() 388 initial_urls = sys.argv[1:] 389 if not initial_urls: 390 initial_urls.append('http://qt.io') 391 for url in initial_urls: 392 main_win.load_url_in_new_tab(QUrl.fromUserInput(url)) 393 exit_code = app.exec_() 394 main_win.write_bookmarks() 395 sys.exit(exit_code) 396