├── .gitignore ├── README.md ├── browser ├── __init__.py ├── about.py ├── errors.py ├── history.py ├── main_window.py ├── printer.py ├── settings.py └── widgets.py ├── browser_screenshot.jpg ├── main.py ├── requirements.txt ├── resources ├── fonts │ └── fa-solid-900.ttf ├── icons │ ├── adobepdf.png │ ├── app_window_ios.png │ ├── arrow-down-12.png │ ├── closetab.png │ ├── closetabbutton.png │ ├── cross.png │ ├── globe.png │ ├── history.png │ ├── home.png │ ├── info.png │ ├── info_24.png │ ├── left-arrow.png │ ├── lock_icon.png │ ├── more.png │ ├── newtab.png │ ├── openclickhtml.png │ ├── paste.png │ ├── printer.png │ ├── printerprev.png │ ├── question.png │ ├── refresh.png │ ├── right-arrow-context-menu.png │ ├── right-arrow.png │ ├── save-disk.png │ ├── security.png │ ├── settings.png │ ├── url.png │ └── warning.png └── logos │ ├── browser.ico │ └── browser.png └── styles ├── about_style.css ├── addr_bar.css ├── history_style.css ├── settings_style.css ├── styles.css └── tab_style.css /.gitignore: -------------------------------------------------------------------------------- 1 | *.spec 2 | .vscode 3 | *.db 4 | webBrowserDB.db 5 | .idea 6 | workspace.xml 7 | __pycache__ 8 | env 9 | Installer 10 | venv 11 | *.swp 12 | *.un~ 13 | main.py~ 14 | browser-screenshot.png 15 | build 16 | dist 17 | Installer.exe 18 | *.exe 19 | Installer.zip 20 | webBrowserDB.db 21 | launch.json 22 | Installer-files 23 | settings.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyQt5 Simple Web Browser 2 | 3 | ![Screenshot](./browser_screenshot.jpg) 4 | 5 | A tabbed browser created by me with Python, PyQt5 module with a bunch of features. 6 | 7 | Tested on: 8 | Python version: Python 3.9 (64 bit) 9 | PyQt5 version: 5.15.4 10 | 11 | ### Install dependencies 12 | 13 | 14 | ``` 15 | python -m pip install -r requirements.txt 16 | ``` 17 | 18 | ### Run python file 19 | 20 | ``` 21 | python main.py 22 | ``` 23 | 24 | ### Some features: 25 | 26 | - Work with multiple tabs. Double click on tab bar to open a new 27 | - Search Google right from the address bar 28 | - Open local files 29 | - save webpages as html 30 | - Save as pdf 31 | - Print webpages 32 | - Copy site url to clipboard feature 33 | - Paste and go feature 34 | - Customize default search engine, startup page and new tab page 35 | -------------------------------------------------------------------------------- /browser/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import sqlite3 5 | 6 | from PyQt5.QtGui import QFontDatabase, QIcon, QFont 7 | 8 | from PyQt5.QtWidgets import QApplication 9 | import browser.main_window 10 | 11 | 12 | # DB to open 13 | connection = sqlite3.connect("BrowserLocalDB.db", check_same_thread=False) 14 | # connection = sqlite3.connect(":memory:") 15 | cursor = connection.cursor() 16 | 17 | # Font 18 | textFont = QFont("Times", 14) 19 | 20 | if os.path.isfile("settings.json"): # If settings file exists, then open it 21 | with open("settings.json", "r") as f: 22 | settings_data = json.load(f) 23 | else: # If settings not exists, then create a new file with default settings 24 | json_data = json.loads( 25 | """ 26 | { 27 | "defaultSearchEngine": "Google", 28 | "startupPage": "https://browser-new-tab.netlify.app", 29 | "newTabPage": "https://browser-new-tab.netlify.app", 30 | "homeButtonPage": "https://browser-new-tab.netlify.app" 31 | } 32 | """ 33 | ) 34 | with open("settings.json", "w") as f: 35 | json.dump(json_data, f, indent=2) 36 | with open("settings.json", "r") as f: 37 | settings_data = json.load(f) 38 | 39 | 40 | def main(): 41 | gui_app = QApplication(sys.argv) 42 | 43 | # Disable shortcut in context menu 44 | gui_app.styleHints().setShowShortcutsInContextMenus(False) 45 | 46 | # Set the window name 47 | QApplication.setApplicationName("Simple Web Browser") 48 | 49 | # Set the window icon 50 | QApplication.setWindowIcon(QIcon(os.path.join("resources", "logos", "browser.png"))) 51 | 52 | # App styles 53 | if os.path.isfile(os.path.join("styles", "styles.css")): 54 | with open(os.path.join("styles", "styles.css")) as f: 55 | gui_app.setStyleSheet(f.read()) 56 | 57 | QFontDatabase.addApplicationFont(os.path.join("fonts", "fa-solid-900.ttf")) 58 | 59 | window = browser.main_window.mainWindow() 60 | 61 | sys.exit(gui_app.exec_()) 62 | -------------------------------------------------------------------------------- /browser/about.py: -------------------------------------------------------------------------------- 1 | import os 2 | from PyQt5.QtGui import QFont, QPixmap 3 | from PyQt5.QtCore import Qt 4 | from PyQt5.QtWidgets import ( 5 | QDialogButtonBox, 6 | QDialog, 7 | QVBoxLayout, 8 | QLabel 9 | ) 10 | 11 | 12 | class AboutDialog(QDialog): 13 | def __init__(self, parent=None, *args, **kwargs): 14 | super(AboutDialog, self).__init__(*args, **kwargs) 15 | 16 | self.layout = QVBoxLayout() 17 | 18 | ok_btn = QDialogButtonBox.Ok 19 | self.button_box = QDialogButtonBox(ok_btn) 20 | 21 | self.init_ui() 22 | 23 | def init_ui(self): 24 | self.button_box.accepted.connect(self.accept) 25 | self.button_box.rejected.connect(self.reject) 26 | 27 | with open(os.path.join("styles", "about_style.css")) as f: 28 | self.button_box.button(QDialogButtonBox.Ok).setStyleSheet(f.read()) 29 | 30 | logo = QLabel() 31 | pixmap = QPixmap(os.path.join("resources", "logos", "browser.png")) 32 | pixmap = pixmap.scaled(80, 80) 33 | logo.setPixmap(pixmap) 34 | self.layout.addWidget(logo) 35 | 36 | title = QLabel("Simple Web Browser") 37 | title.setFont(QFont("Times", 20)) 38 | 39 | self.layout.addWidget(title) 40 | 41 | lbl1 = QLabel( 42 | '
Version 2.4
Copyright ©2021 Made by Samin Sakur.
' 43 | ) 44 | lbl1.setFont(QFont("Times", 10)) 45 | lbl1.setOpenExternalLinks(True) 46 | self.layout.addWidget(lbl1) 47 | 48 | github_pg = QLabel( 49 | 'Learn More ' 50 | ) 51 | 52 | font = QFont("Font Awesome 5 Free Solid", 10) 53 | github_pg.setFont(font) 54 | github_pg.setOpenExternalLinks(True) 55 | self.layout.addWidget(github_pg) 56 | 57 | for i in range(0, self.layout.count()): 58 | self.layout.itemAt(i).setAlignment(Qt.AlignHCenter) 59 | 60 | self.layout.addWidget(self.button_box) 61 | 62 | self.setLayout(self.layout) 63 | 64 | self.setWindowFlags(Qt.MSWindowsFixedSizeDialogHint) 65 | self.resize(400, 250) 66 | self.setMaximumHeight(300) 67 | self.setMaximumWidth(500) 68 | self.setWindowTitle("About") 69 | -------------------------------------------------------------------------------- /browser/errors.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QMessageBox 2 | 3 | 4 | class fileErrorDialog(QMessageBox): 5 | def __init__(self, *args, **kwargs): 6 | super(fileErrorDialog, self).__init__(*args, **kwargs) 7 | 8 | self.setText("Wrong file entered, Enter a correct file and try again.") 9 | self.setIcon(QMessageBox.Critical) 10 | 11 | self.setWindowTitle("Please enter a correct file") 12 | self.show() 13 | 14 | 15 | class errorMsg(QMessageBox): 16 | def __init__(self, text: str = "An internal error occurred!"): 17 | super(errorMsg, self).__init__() 18 | 19 | self.setText(text) 20 | self.setIcon(QMessageBox.Critical) 21 | 22 | self.setWindowTitle("Error!") 23 | self.show() 24 | -------------------------------------------------------------------------------- /browser/history.py: -------------------------------------------------------------------------------- 1 | from PyQt5 import QtCore 2 | from PyQt5.QtWidgets import QGridLayout, QListWidget, QPushButton, QWidget, QLabel 3 | import browser 4 | import browser.main_window 5 | import os 6 | 7 | 8 | class HistoryWindow(QWidget): 9 | def __init__(self): 10 | super().__init__() 11 | 12 | titleLbl = QLabel("History") 13 | titleLbl.setFont(browser.textFont) 14 | 15 | clearBtn = QPushButton("Clear") 16 | clearBtn.setObjectName("ClearButnHistory") 17 | clearBtn.setFont(browser.textFont) 18 | clearBtn.clicked.connect(self.clearHistory) 19 | 20 | self.historyList = QListWidget() 21 | # self.historyList.horizontalScrollBar().setEnabled(False) 22 | 23 | self.fillHistoryList() 24 | 25 | self.historyList.itemClicked.connect(self.goClickedLink) 26 | 27 | with open(os.path.join("styles", "history_style.css")) as f: 28 | style = f.read() 29 | clearBtn.setStyleSheet(style) 30 | self.historyList.setStyleSheet(style) 31 | self.historyList.horizontalScrollBar().setStyleSheet(style) 32 | self.historyList.verticalScrollBar().setStyleSheet(style) 33 | 34 | layout = QGridLayout() 35 | 36 | layout.addWidget(titleLbl, 0, 0) 37 | layout.addWidget(clearBtn, 0, 1) 38 | layout.addWidget(self.historyList, 1, 0, 1, 2) 39 | layout.setContentsMargins(0, 0, 0, 0) 40 | self.setLayout(layout) 41 | 42 | def fillHistoryList(self): 43 | data = browser.cursor.execute("SELECT * FROM history") 44 | siteInfoList = data.fetchall() 45 | for i in range(len(siteInfoList) - 1, -1, -1): 46 | siteInfo = siteInfoList[i][1] + " - " + siteInfoList[i][3] 47 | self.historyList.addItem(siteInfo) 48 | 49 | def goClickedLink(self, item): 50 | siteName = item.text() 51 | visitDate = siteName[len(siteName) - 19 :] 52 | siteInfoFromDB = browser.cursor.execute( 53 | "SELECT * FROM history WHERE date = ?", [visitDate] 54 | ) 55 | try: 56 | url = siteInfoFromDB.fetchall()[0][2] 57 | w = browser.main_window.mainWindow() 58 | w.openSiteHistoryClicked( 59 | QtCore.QUrl(url), str(siteName) 60 | ) # open selected url 61 | except: 62 | self.close() 63 | 64 | self.close() 65 | 66 | def clearHistory(self): 67 | self.historyList.clear() 68 | browser.cursor.execute("DELETE FROM history") 69 | browser.connection.commit() 70 | -------------------------------------------------------------------------------- /browser/main_window.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWebEngineWidgets import ( 2 | QWebEngineDownloadItem, 3 | QWebEngineSettings, 4 | QWebEngineView, 5 | ) 6 | from PyQt5.QtWidgets import ( 7 | QMainWindow, 8 | QPushButton, 9 | QShortcut, 10 | QToolBar, 11 | QMenu, 12 | QAction, 13 | QFileDialog, 14 | ) 15 | from PyQt5.QtCore import QSize, QUrl, Qt 16 | from PyQt5.QtGui import QIcon, QPixmap 17 | from PyQt5 import QtCore 18 | from PyQt5 import QtGui 19 | import browser.widgets 20 | import browser.printer 21 | import browser.errors 22 | import browser.about 23 | import browser.history 24 | import browser.settings 25 | import browser 26 | import sys 27 | import os 28 | import re 29 | import pyperclip as pc 30 | import datetime 31 | 32 | 33 | # Regular expressions to match urls 34 | pattern = re.compile( 35 | r"^(http|https)?:?(\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)" 36 | ) 37 | without_http_pattern = re.compile( 38 | r"[\-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)" 39 | ) 40 | file_pattern = re.compile(r"^file://") 41 | 42 | 43 | class mainWindow(QMainWindow): 44 | def __init__(self, *args, **kwargs): 45 | super(mainWindow, self).__init__(*args, **kwargs) 46 | self.init_ui() 47 | 48 | def init_ui(self): 49 | self.tabs = browser.widgets.Tabs() # create tabs 50 | 51 | # create history table 52 | browser.cursor.execute( 53 | """CREATE TABLE IF NOT EXISTS "history" ( 54 | "id" INTEGER, 55 | "title" TEXT, 56 | "url" TEXT, 57 | "date" TEXT, 58 | PRIMARY KEY("id") 59 | )""" 60 | ) 61 | 62 | # Add new tab when tab tab is doubleclicked 63 | self.tabs.tabBarDoubleClicked.connect(self.tab_open_doubleclick) 64 | 65 | # To connect to a function when current tab has been changed 66 | self.tabs.currentChanged.connect(self.tab_changed) 67 | 68 | # Function to handle tab closing 69 | self.tabs.tabCloseRequested.connect(self.close_current_tab) 70 | 71 | # open new tab when Ctrl+T pressed 72 | AddNewTabKeyShortcut = QShortcut("Ctrl+T", self) 73 | AddNewTabKeyShortcut.activated.connect( 74 | lambda: self.add_new_tab( 75 | QtCore.QUrl(browser.settings_data["newTabPage"], "New tab") 76 | ) 77 | ) 78 | 79 | # Close current tab on Ctrl+W 80 | CloseCurrentTabKeyShortcut = QShortcut("Ctrl+W", self) 81 | CloseCurrentTabKeyShortcut.activated.connect( 82 | lambda: self.close_current_tab(self.tabs.currentIndex()) 83 | ) 84 | 85 | # Exit browser on shortcut Ctrl+Shift+W 86 | ExitBrowserShortcutKey = QShortcut("Ctrl+Shift+W", self) 87 | ExitBrowserShortcutKey.activated.connect(sys.exit) 88 | 89 | # nav bar 90 | self.navbar = QToolBar() 91 | self.navbar.setMovable(False) 92 | self.addToolBar(self.navbar) 93 | 94 | # back button 95 | back_btn = QPushButton(self) 96 | back_btn.setObjectName("back_btn") 97 | back_btn.setIcon( 98 | QtGui.QIcon(os.path.join("resources", "icons", "left-arrow.png")) 99 | ) 100 | back_btn.setToolTip("Back to previous page") 101 | back_btn.setShortcut("Alt+Left") 102 | back_btn.clicked.connect(self.navigate_back_tab) 103 | self.navbar.addWidget(back_btn) 104 | 105 | # forward button 106 | forward_butn = QPushButton(self) 107 | forward_butn.setObjectName("forward_butn") 108 | forward_butn.setIcon( 109 | QtGui.QIcon(os.path.join("resources", "icons", "right-arrow.png")) 110 | ) 111 | forward_butn.setToolTip("Go forward") 112 | forward_butn.setShortcut("Alt+Right") 113 | forward_butn.clicked.connect(self.forward_tab) 114 | self.navbar.addWidget(forward_butn) 115 | 116 | # Refresh button 117 | self.reload_butn = QPushButton(self) 118 | self.reload_butn.setObjectName("reload_butn") 119 | self.reload_butn.setToolTip("Reload current page") 120 | self.reload_butn.setShortcut("Ctrl+R") 121 | self.reload_butn.resize(QSize(50, 50)) 122 | self.reload_butn.setIcon( 123 | QtGui.QIcon(os.path.join("resources", "icons", "refresh.png")) 124 | ) 125 | self.reload_butn.clicked.connect(self.reload_tab) 126 | 127 | self.stop_btn = QPushButton(self) 128 | self.stop_btn.setObjectName("stop_butn") 129 | self.stop_btn.setToolTip("Stop loading current page") 130 | self.stop_btn.setShortcut("Escape") 131 | self.stop_btn.setIcon(QIcon(os.path.join("resources", "icons", "cross.png"))) 132 | self.stop_btn.clicked.connect(self.stop_loading_tab) 133 | 134 | # Added stop button 135 | self.stop_action = self.navbar.addWidget(self.stop_btn) 136 | 137 | # Added reload button 138 | self.reload_action = self.navbar.addWidget(self.reload_butn) 139 | 140 | # Home button 141 | self.home_button = QPushButton(self) 142 | self.home_button.setObjectName("home_button") 143 | self.home_button.setToolTip("Back to home") 144 | self.home_button.setIcon( 145 | QtGui.QIcon(os.path.join("resources", "icons", "home.png")) 146 | ) 147 | self.home_button.clicked.connect(self.goToHome) 148 | self.navbar.addWidget(self.home_button) 149 | 150 | # Add Address bar 151 | self.url_bar = browser.widgets.AddressBar() 152 | self.url_bar.initAddressBar() 153 | self.url_bar.setFrame(False) 154 | self.url_bar.returnPressed.connect(self.navigate_to_url) 155 | self.url_bar.setShortcutEnabled(True) 156 | self.url_bar.setToolTip(self.url_bar.text()) 157 | 158 | # Set focus on the Addressbar by pressing Ctrl+E 159 | FocusOnAddressBar = QShortcut("Ctrl+E", self) 160 | FocusOnAddressBar.activated.connect(self.url_bar.setFocus) 161 | 162 | # Set stop action to be invisible 163 | self.stop_action.setVisible(False) 164 | 165 | # Add a separator 166 | self.navbar.addSeparator() 167 | 168 | # Shows ssl security icon 169 | self.httpsicon = browser.widgets.SSLIcon() 170 | 171 | # Add http icon to the navbar bar 172 | self.navbar.addWidget(self.httpsicon) 173 | 174 | # Add Address Bar to the navbar 175 | self.navbar.addWidget(self.url_bar) 176 | 177 | # The context menu 178 | context_menu = QMenu(self) 179 | 180 | # Set the object's name 181 | context_menu.setObjectName("ContextMenu") 182 | 183 | # Button for the three dot context menu button 184 | ContextMenuButton = QPushButton(self) 185 | ContextMenuButton.setObjectName("ContextMenuButton") 186 | 187 | # Enable three dot menu by pressing Alt+F 188 | ContextMenuButton.setShortcut("Alt+F") 189 | 190 | # Give the three dot image to the Qpushbutton 191 | ContextMenuButton.setIcon( 192 | QIcon(os.path.join("resources", "icons", "more.png")) 193 | ) # Add icon 194 | ContextMenuButton.setObjectName("ContextMenuTriggerButn") 195 | ContextMenuButton.setToolTip("More") 196 | 197 | # Add the context menu to the three dot context menu button 198 | ContextMenuButton.setMenu(context_menu) 199 | 200 | """Actions of the three dot context menu""" 201 | 202 | # Add new tab 203 | newTabAction = QAction("New tab", self) 204 | newTabAction.setIcon( 205 | QtGui.QIcon(os.path.join("resources", "icons", "newtab.png")) 206 | ) 207 | newTabAction.triggered.connect( 208 | lambda: self.add_new_tab( 209 | QUrl(browser.settings_data["newTabPage"]), "Homepage" 210 | ) 211 | ) 212 | newTabAction.setToolTip("Add a new tab") 213 | context_menu.addAction(newTabAction) 214 | 215 | # New window action 216 | newWindowAction = QAction("New window", self) 217 | newWindowAction.setIcon( 218 | QtGui.QIcon(os.path.join("resources", "icons", "app_window_ios.png")) 219 | ) 220 | newWindowAction.triggered.connect(self.CreateNewWindow) 221 | context_menu.addAction(newWindowAction) 222 | 223 | # Close tab action 224 | CloseTabAction = QAction("Close tab", self) 225 | CloseTabAction.setIcon( 226 | QIcon(os.path.join("resources", "icons", "closetab.png")) 227 | ) 228 | CloseTabAction.triggered.connect( 229 | lambda: self.close_current_tab(self.tabs.currentIndex()) 230 | ) 231 | CloseTabAction.setToolTip("Close current tab") 232 | context_menu.addAction(CloseTabAction) 233 | 234 | # A separator 235 | context_menu.addSeparator() 236 | 237 | # Another separator 238 | context_menu.addSeparator() 239 | 240 | # Feature to copy site url 241 | CopySiteAddress = QAction( 242 | QtGui.QIcon(os.path.join("resources", "icons", "url.png")), 243 | "Copy site url", 244 | self, 245 | ) 246 | CopySiteAddress.triggered.connect(self.CopySiteLink) 247 | CopySiteAddress.setToolTip("Copy current site address") 248 | context_menu.addAction(CopySiteAddress) 249 | 250 | # Fetaure to go to copied site url 251 | PasteAndGo = QAction( 252 | QtGui.QIcon(os.path.join("resources", "icons", "paste.png")), 253 | "Paste and go", 254 | self, 255 | ) 256 | PasteAndGo.triggered.connect(self.PasteUrlAndGo) 257 | PasteAndGo.setToolTip("Go to the an url copied to your clipboard") 258 | context_menu.addAction(PasteAndGo) 259 | 260 | # A separator 261 | context_menu.addSeparator() 262 | 263 | # View history 264 | ViewHistory = QAction("History", self) 265 | ViewHistory.setIcon(QIcon(os.path.join("resources", "icons", "history.png"))) 266 | ViewHistory.triggered.connect(self.openHistory) 267 | ViewHistory.setShortcut("Ctrl+h") 268 | context_menu.addAction(ViewHistory) 269 | 270 | # Open page 271 | OpenPgAction = QAction("Open", self) 272 | OpenPgAction.setIcon( 273 | QtGui.QIcon(os.path.join("resources", "icons", "openclickhtml.png")) 274 | ) 275 | OpenPgAction.setToolTip("Open a file in this browser") 276 | OpenPgAction.setShortcut("Ctrl+O") 277 | OpenPgAction.triggered.connect(self.open_local_file) 278 | context_menu.addAction(OpenPgAction) 279 | 280 | # Save page as 281 | SavePageAs = QAction("Save page as", self) 282 | SavePageAs.setIcon( 283 | QtGui.QIcon(os.path.join("resources", "icons", "save-disk.png")) 284 | ) 285 | SavePageAs.setToolTip("Save current page to this device") 286 | SavePageAs.setShortcut("Ctrl+S") 287 | SavePageAs.triggered.connect(self.save_page) 288 | context_menu.addAction(SavePageAs) 289 | 290 | # Print this page action 291 | PrintThisPageAction = QAction("Print this page", self) 292 | PrintThisPageAction.setIcon( 293 | QtGui.QIcon(os.path.join("resources", "icons", "printer.png")) 294 | ) 295 | PrintThisPageAction.triggered.connect(self.print_this_page) 296 | PrintThisPageAction.setShortcut("Ctrl+P") 297 | PrintThisPageAction.setToolTip("Print current page") 298 | context_menu.addAction(PrintThisPageAction) 299 | 300 | # Print with preview 301 | PrintPageWithPreview = QAction( 302 | QtGui.QIcon(os.path.join("resources", "icons", "printerprev.png")), 303 | "Print page with preview", 304 | self, 305 | ) 306 | PrintPageWithPreview.triggered.connect(self.PrintWithPreview) 307 | PrintPageWithPreview.setShortcut("Ctrl+Shift+P") 308 | context_menu.addAction(PrintPageWithPreview) 309 | 310 | # Save page as PDF 311 | SavePageAsPDF = QAction( 312 | QtGui.QIcon(os.path.join("resources", "icons", "adobepdf.png")), 313 | "Save as PDF", 314 | self, 315 | ) 316 | SavePageAsPDF.triggered.connect(self.save_as_pdf) 317 | context_menu.addAction(SavePageAsPDF) 318 | 319 | context_menu.addSeparator() 320 | 321 | # Settings widget: 322 | userSettingsAction = QAction( 323 | QtGui.QIcon(os.path.join("resources", "icons", "settings.png")), 324 | "Settings", 325 | self, 326 | ) 327 | userSettingsAction.triggered.connect(self.openSettings) 328 | context_menu.addAction(userSettingsAction) 329 | 330 | # The help submenu 331 | HelpMenu = QMenu("Help", self) 332 | HelpMenu.setObjectName("HelpMenu") 333 | HelpMenu.setIcon(QIcon(os.path.join("resources", "icons", "question.png"))) 334 | 335 | # About action 336 | AboutAction = QAction("About this browser", self) 337 | AboutAction.setIcon(QIcon(os.path.join("resources", "icons", "info.png"))) 338 | AboutAction.triggered.connect(self.about) 339 | HelpMenu.addAction(AboutAction) 340 | 341 | # Visit action 342 | VisitGithubAction = QAction("Visit Github", self) 343 | VisitGithubAction.triggered.connect(self.visitGithub) 344 | HelpMenu.addAction(VisitGithubAction) 345 | 346 | context_menu.addMenu(HelpMenu) 347 | 348 | # Add a separator 349 | context_menu.addSeparator() 350 | 351 | # Close browser 352 | CloseBrowser = QAction("Close browser", self) 353 | CloseBrowser.triggered.connect(lambda: sys.exit()) 354 | context_menu.addAction(CloseBrowser) 355 | 356 | """ 357 | Set menu for the button 358 | ContextMenuButton.add 359 | Add the context menu to the navbar 360 | """ 361 | 362 | self.navbar.addWidget(ContextMenuButton) 363 | 364 | # Stuffs to see at startup 365 | self.add_new_tab(QUrl(browser.settings_data["startupPage"]), "Homepage") 366 | 367 | # Set the address focus 368 | self.url_bar.setFocus() 369 | 370 | # what to display on the window 371 | self.setCentralWidget(self.tabs) 372 | 373 | # Stuffs to set the window 374 | self.showMaximized() 375 | 376 | # Set minimum size 377 | self.setMinimumWidth(400) 378 | 379 | """ 380 | Instead of managing 2 slots associated with the progress and completion of loading, 381 | only one of them should be used since, for example, the associated slot is also called when 382 | it is loaded at 100% so it could be hidden since it can be invoked together with finished. 383 | """ 384 | 385 | @QtCore.pyqtSlot(int) 386 | def loadProgressHandler(self, prog): 387 | if self.tabs.currentWidget() is not self.sender(): 388 | return 389 | 390 | loading = prog < 100 391 | 392 | self.stop_action.setVisible(loading) 393 | self.reload_action.setVisible(not loading) 394 | 395 | # funcion to navigate to home when home icon is pressed 396 | 397 | def goToHome(self): 398 | self.tabs.currentWidget().setUrl(QUrl(browser.settings_data["homeButtonPage"])) 399 | 400 | # Define open a new window 401 | 402 | def CreateNewWindow(self): 403 | window = mainWindow() 404 | window.show() 405 | 406 | # Copy url of currently viewed page to clipboard 407 | def CopySiteLink(self): 408 | pc.copy(self.tabs.currentWidget().url().toString()) 409 | 410 | # Adds a new tab and load the content of the clipboard 411 | 412 | def PasteUrlAndGo(self): 413 | self.add_new_tab(QUrl(pc.paste()), self.tabs.currentWidget().title()) 414 | 415 | # Remove this if you don't need it 416 | 417 | def visitGithub(self): 418 | self.add_new_tab( 419 | QUrl("https://github.com/saminsakur/PyQt5BrowserBuild"), "Github" 420 | ) 421 | 422 | # navigate backward tab 423 | 424 | def navigate_back_tab(self): 425 | self.tabs.currentWidget().back() 426 | 427 | # go forward tab 428 | 429 | def forward_tab(self): 430 | self.tabs.currentWidget().forward() 431 | 432 | # reload tab 433 | 434 | def reload_tab(self): 435 | self.tabs.currentWidget().reload() 436 | 437 | # stop load current tab 438 | 439 | def stop_loading_tab(self): 440 | if self.tabs.currentWidget() is None: 441 | return 442 | 443 | self.tabs.currentWidget().stop() 444 | 445 | """ 446 | Functions to open a local file and save a website to user's local storage 447 | """ 448 | 449 | # Function to open a local file 450 | 451 | def open_local_file(self): 452 | filename, _ = QFileDialog.getOpenFileName( 453 | parent=self, 454 | caption="Open file", 455 | directory="", 456 | filter="Hypertext Markup Language (*.htm *.html *.mhtml);;All files (*.*)", 457 | ) 458 | if filename: 459 | try: 460 | with open(filename, "r", encoding="utf8") as f: 461 | opened_file = f.read() 462 | self.tabs.currentWidget().setHtml(opened_file) 463 | 464 | except: 465 | dlg = browser.errors.fileErrorDialog() 466 | dlg.exec_() 467 | 468 | self.url_bar.setText(filename) 469 | 470 | # Function to save current site to user's local storage 471 | 472 | def save_page(self): 473 | filepath, filter = QFileDialog.getSaveFileName( 474 | parent=self, 475 | caption="Save Page As", 476 | directory="", 477 | filter="Webpage, complete (*.htm *.html);;Hypertext Markup Language (*.htm *.html);;All files (*.*)", 478 | ) 479 | try: 480 | if filter == "Hypertext Markup Language (*.htm *.html)": 481 | self.tabs.currentWidget().page().save( 482 | filepath, format=QWebEngineDownloadItem.MimeHtmlSaveFormat 483 | ) 484 | 485 | elif filter == "Webpage, complete (*.htm *.html)": 486 | self.tabs.currentWidget().page().save( 487 | filepath, format=QWebEngineDownloadItem.CompleteHtmlSaveFormat 488 | ) 489 | 490 | except: 491 | self.showErrorDlg() 492 | 493 | # Print handler 494 | def print_this_page(self): 495 | try: 496 | handler_print = browser.printer.PrintHandler() 497 | handler_print.setPage(self.tabs.currentWidget().page()) 498 | handler_print.print() 499 | 500 | except: 501 | self.showErrorDlg() 502 | 503 | # Print page with preview 504 | def PrintWithPreview(self): 505 | handler = browser.printer.PrintHandler() 506 | handler.setPage(self.tabs.currentWidget().page()) 507 | handler.printPreview() 508 | 509 | # Save as pdf 510 | def save_as_pdf(self): 511 | filename, filter = QFileDialog.getSaveFileName( 512 | parent=self, caption="Save as", filter="PDF File (*.pdf);;All files (*.*)" 513 | ) 514 | 515 | self.tabs.currentWidget().page().printToPdf(filename) 516 | 517 | # doubleclick on empty space for new tab 518 | def tab_open_doubleclick(self, i): 519 | if i == -1: # No tab under the click 520 | self.add_new_tab(QUrl(browser.settings_data["newTabPage"]), label="New tab") 521 | 522 | # to update the tab 523 | def tab_changed(self, i): 524 | qurl = self.tabs.currentWidget().url() 525 | self.update_urlbar(qurl, self.tabs.currentWidget()) 526 | self.update_title(self.tabs.currentWidget()) 527 | 528 | # to close current tab 529 | def close_current_tab(self, i): 530 | if self.tabs.count() < 2: 531 | self.close() 532 | 533 | self.tabs.removeTab(i) 534 | 535 | # Update window title 536 | def update_title(self, browser): 537 | if browser != self.tabs.currentWidget(): 538 | return 539 | 540 | title = self.tabs.currentWidget().page().title() 541 | 542 | if 0 > len(title): 543 | self.setWindowTitle("{} - Simple Web Browser".format(title)) 544 | 545 | else: 546 | self.setWindowTitle("Simple Web Browser") 547 | 548 | # function to add new tab 549 | def add_new_tab(self, qurl=None, label="Blank"): 550 | if qurl is None: 551 | qurl = QUrl(browser.settings_data["newTabPage"]) 552 | 553 | _browser = QWebEngineView() # Define the main webview to browser the internet 554 | 555 | # Set page 556 | _browser.setPage(browser.widgets.customWebEnginePage(_browser)) 557 | 558 | # Full screen enable 559 | _browser.settings().setAttribute( 560 | QWebEngineSettings.FullScreenSupportEnabled, True 561 | ) 562 | _browser.page().fullScreenRequested.connect(lambda request: request.accept()) 563 | 564 | _browser.loadProgress.connect(self.loadProgressHandler) 565 | 566 | _browser.page().WebAction() 567 | 568 | _browser.settings().setAttribute(QWebEngineSettings.ScreenCaptureEnabled, True) 569 | 570 | i = self.tabs.addTab(_browser, label) 571 | self.tabs.setCurrentIndex(i) 572 | 573 | _browser.load(qurl) 574 | self.url_bar.setFocus() 575 | 576 | # update url when it's from the correct tab 577 | _browser.urlChanged.connect( 578 | lambda qurl, browser=_browser: self.update_urlbar(qurl, browser) 579 | ) 580 | 581 | _browser.loadFinished.connect( 582 | lambda _, i=i, browser=_browser: self.tabs.setTabText( 583 | i, browser.page().title() 584 | ) 585 | ) 586 | 587 | # update history when loading finished 588 | _browser.page().loadFinished.connect(self.updateHistory) 589 | 590 | def showErrorDlg(self): 591 | dlg = browser.errors.errorMsg() 592 | dlg.exec_() 593 | 594 | def about(self): 595 | self.AboutDialogue = browser.about.AboutDialog() 596 | self.AboutDialogue.show() 597 | 598 | # Update address bar to show current pages's url 599 | def update_urlbar(self, q, _browser=None): 600 | if _browser != self.tabs.currentWidget(): 601 | # if signal is not from the current tab, then ignore 602 | return 603 | 604 | if q.toString() == browser.settings_data["newTabPage"]: 605 | self.httpsicon.setPixmap( 606 | QPixmap(os.path.join("resources", "icons", "info_24.png")) 607 | ) 608 | self.httpsicon.setToolTip("This is browser's new tab page") 609 | self.url_bar.clear() 610 | 611 | else: 612 | if q.scheme() == "https": 613 | # secure padlock icon 614 | self.httpsicon.setPixmap( 615 | QPixmap(os.path.join("resources", "icons", "security.png")) 616 | ) 617 | self.httpsicon.setToolTip( 618 | "Connection to this is is secure\n\nThis site have a valid certificate" 619 | ) 620 | 621 | elif q.scheme() == "file": 622 | self.httpsicon.setPixmap( 623 | QPixmap(os.path.join("resources", "icons", "info_24.png")) 624 | ) 625 | self.httpsicon.setToolTip("You are viewing a local or shared file") 626 | 627 | elif q.scheme() == "data": 628 | self.httpsicon.setPixmap( 629 | QPixmap(os.path.join("resources", "icons", "info_24.png")) 630 | ) 631 | self.httpsicon.setToolTip("You are viewing a local or shared file") 632 | 633 | else: 634 | # Set insecure padlock 635 | self.httpsicon.setPixmap( 636 | QPixmap(os.path.join("resources", "icons", "warning.png")) 637 | ) 638 | self.httpsicon.setToolTip("Connection to this site may not be secured") 639 | 640 | # if q.toString() == browser.settings_data["newTabPage"]: 641 | # self.url_bar.clear() 642 | 643 | self.url_bar.setCursorPosition(0) 644 | 645 | # function to search google from the search box 646 | def searchWeb(self, text): 647 | Engine = browser.settings_data["defaultSearchEngine"] 648 | if text: 649 | if Engine == "Google": 650 | return "https://www.google.com/search?q=" + "+".join(text.split()) 651 | 652 | elif Engine == "Yahoo": 653 | return "https://search.yahoo.com/search?q=" + "+".join(text.split()) 654 | 655 | elif Engine == "Bing": 656 | return "https://www.bing.com/search?q=" + "+".join(text.split()) 657 | 658 | elif Engine == "DuckDuckGo": 659 | return "https://duckduckgo.com/?q=" + "+".join(text.split()) 660 | 661 | """ 662 | function to navigate to url, if the url ends with the domains from the domains tuple, 663 | then "http://" will be added after what the user have written if not, then it will call 664 | the searchWeb() function to search bing directly from the search box 665 | """ 666 | 667 | def navigate_to_url(self): 668 | in_url = self.url_bar.text() 669 | url = "" 670 | """ if the text in the search box endswith one of the domain in the domains tuple, then "http://" will be added 671 | if the text is pre "http://" or "https://" added, then not""" 672 | # [0-9A-Za-z]+\.+[A-Za-z0-9]{2} 673 | if len(str(in_url)) < 1: 674 | return 675 | 676 | if self.tabs.currentWidget is None: # To avoid exception 677 | # If QTabWidget's currentwidget is none, the ignore 678 | return 679 | 680 | if file_pattern.search(in_url): 681 | file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), in_url)) 682 | local_url = QUrl.fromLocalFile(file_path) 683 | self.tabs.currentWidget().load(local_url) 684 | 685 | elif without_http_pattern.search(in_url) and any( 686 | [i in in_url for i in ["http://", "https://"]] 687 | ): 688 | url = in_url 689 | 690 | elif pattern.search(in_url) and not any( 691 | i in in_url for i in ("http://", "https://", "file:///") 692 | ): 693 | url = "http://" + in_url 694 | 695 | # this will search google 696 | elif not "/" in in_url: 697 | url = self.searchWeb(in_url) 698 | 699 | self.tabs.currentWidget().load(QUrl.fromUserInput(url)) 700 | 701 | def updateHistory(self): 702 | title = self.tabs.currentWidget().page().title() 703 | url = str(self.tabs.currentWidget().page().url()) 704 | url = url[19 : len(url) - 2] 705 | hour = datetime.datetime.now().strftime("%X") 706 | day = datetime.datetime.now().strftime("%x") 707 | date = hour + " - " + day 708 | 709 | data = browser.cursor.execute("SELECT * FROM history") 710 | siteInfoList = data.fetchall() 711 | 712 | for i in range(len(siteInfoList)): 713 | if url == siteInfoList[i][2]: 714 | browser.cursor.execute("DELETE FROM history WHERE url = ?", [url]) 715 | 716 | browser.cursor.execute( 717 | "INSERT INTO history (title,url,date) VALUES (:title,:url,:date)", 718 | {"title": title, "url": url, "date": date}, 719 | ) 720 | 721 | browser.connection.commit() 722 | 723 | def openHistory(self): 724 | self.historyWindow = browser.history.HistoryWindow() 725 | self.historyWindow.setWindowFlags(Qt.Popup) 726 | self.historyWindow.setGeometry( 727 | int(self.tabs.currentWidget().frameGeometry().width() / 2 + 400), 728 | 70, 729 | 300, 730 | 500, 731 | ) 732 | self.historyWindow.setContentsMargins(0, 0, 0, 0) 733 | self.historyWindow.setStyleSheet( 734 | """ 735 | background-color:#edf4f7; 736 | """ 737 | ) 738 | self.historyWindow.show() 739 | 740 | def openSiteHistoryClicked(self, url, *args): 741 | self.tabs.currentWidget().load(url) 742 | 743 | def openSettings(self): 744 | self.userSettingswindow = browser.settings.UserSettings() 745 | self.userSettingswindow.setWindowFlag(Qt.MSWindowsFixedSizeDialogHint) 746 | self.userSettingswindow.show() 747 | 748 | -------------------------------------------------------------------------------- /browser/printer.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import QObject, pyqtSlot, QEventLoop, QPointF 2 | from PyQt5.QtGui import QPainter 3 | from PyQt5.QtPrintSupport import QPrinter, QPrintDialog, QPrintPreviewDialog 4 | from PyQt5.QtWidgets import QDialog, QProgressDialog, QProgressBar 5 | 6 | 7 | class PrintHandler(QObject): 8 | def __init__(self, parent=None): 9 | super().__init__(parent) 10 | self.m_page = None 11 | self.m_inPrintPreview = False 12 | 13 | def setPage(self, page): 14 | assert not self.m_page 15 | self.m_page = page 16 | self.m_page.printRequested.connect(self.printPreview) 17 | 18 | @pyqtSlot() 19 | def print(self): 20 | printer = QPrinter(QPrinter.HighResolution) 21 | dialog = QPrintDialog(printer, self.m_page.view()) 22 | if dialog.exec_() != QDialog.Accepted: 23 | return 24 | self.printDocument(printer) 25 | 26 | @pyqtSlot() 27 | def printPreview(self): 28 | if not self.m_page: 29 | return 30 | if self.m_inPrintPreview: 31 | return 32 | self.m_inPrintPreview = True 33 | printer = QPrinter() 34 | preview = QPrintPreviewDialog(printer, self.m_page.view()) 35 | preview.paintRequested.connect(self.printDocument) 36 | preview.exec() 37 | self.m_inPrintPreview = False 38 | 39 | @pyqtSlot(QPrinter) 40 | def printDocument(self, printer): 41 | loop = QEventLoop() 42 | result = False 43 | 44 | def printPreview(success): 45 | nonlocal result 46 | result = success 47 | loop.quit() 48 | 49 | # progressbar to show loading 50 | progressbar = QProgressDialog(self.m_page.view()) 51 | progressbar.findChild(QProgressBar).setTextVisible(False) 52 | progressbar.setLabelText("Please wait...") 53 | progressbar.setRange(0, 0) 54 | progressbar.show() 55 | progressbar.canceled.connect(loop.quit) 56 | self.m_page.print(printer, printPreview) 57 | loop.exec_() 58 | progressbar.close() 59 | 60 | if not result: 61 | painter = QPainter() 62 | if painter.begin(printer): 63 | font = painter.font() 64 | font.setPixelSize(20) 65 | painter.setFont(font) 66 | painter.drawText( 67 | QPointF(10, 25), "We could not generate print preview." 68 | ) 69 | painter.end() 70 | -------------------------------------------------------------------------------- /browser/settings.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | from PyQt5 import QtWidgets, QtGui, QtCore 4 | from PyQt5.QtWidgets import QWidget 5 | 6 | import browser 7 | 8 | 9 | class UserSettings(QtWidgets.QWidget): 10 | def __init__(self): 11 | super().__init__() 12 | 13 | self.settings_data = browser.settings_data 14 | self.default_search_engine = self.settings_data["defaultSearchEngine"] 15 | self.mainWidget = QWidget(self) 16 | 17 | self.init_ui() 18 | self.retranslateUi() 19 | 20 | def init_ui(self): 21 | self.resize(706, 485) 22 | self.addDefaultSearchEngineSelector() 23 | 24 | self.title_label_size = 10 25 | 26 | # Add settings title 27 | self.label_2 = QtWidgets.QLabel(self.mainWidget) 28 | self.label_2.setGeometry(QtCore.QRect(10, 10, 71, 21)) 29 | font = QtGui.QFont() 30 | font.setFamily("Segoe UI") 31 | font.setPointSize(self.title_label_size) 32 | font.setBold(False) 33 | font.setItalic(False) 34 | font.setWeight(50) 35 | self.label_2.setFont(font) 36 | self.label_2.setStyleSheet('font: 12pt "Segoe UI";') 37 | self.label_2.setObjectName("label_2") 38 | 39 | # self.check_box_enable = QtWidgets.QCheckBox("Use same page used in home button in all", self.mainWidget) 40 | # self.check_box_enable.setGeometry(QtCore.QRect(10, 130, 70, 17)) 41 | 42 | self.startup_page = QtWidgets.QLineEdit(self.mainWidget) 43 | self.startup_page.setGeometry(QtCore.QRect(480, 150, 211, 33)) 44 | self.startup_page.setText(self.settings_data["startupPage"]) 45 | self.startup_page.setObjectName("startup_page") 46 | 47 | # On startup section 48 | self.label_3 = QtWidgets.QLabel(self.mainWidget) 49 | self.label_3.setGeometry(QtCore.QRect(10, 130, 91, 21)) 50 | font = QtGui.QFont() 51 | font.setFamily("Segoe UI") 52 | font.setPointSize(self.title_label_size) 53 | font.setBold(True) 54 | font.setWeight(75) 55 | self.label_3.setFont(font) 56 | self.label_3.setObjectName("label_3") 57 | 58 | self.label = QtWidgets.QLabel(self.mainWidget) 59 | self.label.setGeometry(QtCore.QRect(10, 60, 171, 21)) 60 | font = QtGui.QFont() 61 | font.setFamily("Segoe UI") 62 | font.setPointSize(self.title_label_size) 63 | font.setBold(True) 64 | font.setWeight(75) 65 | self.label.setFont(font) 66 | self.label.setObjectName("label") 67 | 68 | # Home button section 69 | self.label_7 = QtWidgets.QLabel(self.mainWidget) 70 | self.label_7.setGeometry(QtCore.QRect(10, 230, 101, 21)) 71 | font = QtGui.QFont() 72 | font.setFamily("Segoe UI") 73 | font.setPointSize(self.title_label_size) 74 | font.setBold(True) 75 | font.setWeight(75) 76 | self.label_7.setFont(font) 77 | self.label_7.setObjectName("label_7") 78 | 79 | # Home button page input 80 | self.home_button_page = QtWidgets.QLineEdit(self.mainWidget) 81 | self.home_button_page.setGeometry(QtCore.QRect(480, 230, 211, 33)) 82 | self.home_button_page.setText(self.settings_data["homeButtonPage"]) 83 | self.home_button_page.setObjectName("home_button_page") 84 | 85 | # New tab open settings 86 | self.label_9 = QtWidgets.QLabel(self.mainWidget) 87 | self.label_9.setGeometry(QtCore.QRect(10, 330, 101, 21)) 88 | font = QtGui.QFont() 89 | font.setFamily("Segoe UI") 90 | font.setPointSize(self.title_label_size) 91 | font.setBold(True) 92 | font.setWeight(75) 93 | self.label_9.setFont(font) 94 | self.label_9.setObjectName("label_9") 95 | 96 | # Page to open on each tab 97 | self.new_tab_page = QtWidgets.QLineEdit(self.mainWidget) 98 | self.new_tab_page.setText(self.settings_data["newTabPage"]) 99 | self.new_tab_page.setGeometry(QtCore.QRect(480, 360, 211, 33)) 100 | self.new_tab_page.setObjectName("new_tab_page") 101 | 102 | # these are all descriptions 103 | self.label_4 = QtWidgets.QLabel(self.mainWidget) 104 | self.label_4.setGeometry(QtCore.QRect(10, 160, 235, 20)) 105 | self.label_4.setObjectName("label_4") 106 | font = QtGui.QFont() 107 | font.setFamily("Segoe UI") 108 | font.setPointSize(10) 109 | self.label_4.setFont(font) 110 | 111 | self.label_5 = QtWidgets.QLabel(self.mainWidget) 112 | self.label_5.setGeometry(QtCore.QRect(10, 90, 270, 20)) 113 | self.label_5.setObjectName("label_5") 114 | font = QtGui.QFont() 115 | font.setFamily("Segoe UI") 116 | font.setPointSize(10) 117 | self.label_5.setFont(font) 118 | 119 | self.label_6 = QtWidgets.QLabel(self.mainWidget) 120 | self.label_6.setGeometry(QtCore.QRect(10, 260, 360, 20)) 121 | self.label_6.setObjectName("label_6") 122 | font = QtGui.QFont() 123 | font.setFamily("Segoe UI") 124 | font.setPointSize(10) 125 | self.label_6.setFont(font) 126 | 127 | self.label_8 = QtWidgets.QLabel(self.mainWidget) 128 | self.label_8.setGeometry(QtCore.QRect(10, 360, 320, 20)) 129 | self.label_8.setObjectName("label_8") 130 | font = QtGui.QFont() 131 | font.setFamily("Segoe UI") 132 | font.setPointSize(10) 133 | self.label_8.setFont(font) 134 | 135 | # Save button 136 | self.save_settings = QtWidgets.QPushButton(self.mainWidget) 137 | self.save_settings.setGeometry(QtCore.QRect(572, 440, 121, 33)) 138 | self.save_settings.setObjectName("save_settings") 139 | self.save_settings.clicked.connect(self.saveChangesToJson) 140 | 141 | # Discard button 142 | self.discard_changes = QtWidgets.QPushButton(self.mainWidget) 143 | self.discard_changes.setGeometry(QtCore.QRect(430, 440, 121, 33)) 144 | self.discard_changes.setObjectName("discard_changes") 145 | self.discard_changes.clicked.connect(self.closeWindow) 146 | 147 | QtCore.QMetaObject.connectSlotsByName(self.mainWidget) 148 | 149 | with open( 150 | os.path.join("styles", "settings_style.css") 151 | ) as f: # Read styles from settings_style.css 152 | self.setStyleSheet(f.read()) 153 | 154 | # Add drop-down menu to select default search engine 155 | def addDefaultSearchEngineSelector(self): 156 | self.searchEngineSelector = QtWidgets.QComboBox(self.mainWidget) 157 | self.searchEngineSelector.setEnabled(True) 158 | self.searchEngineSelector.setGeometry(QtCore.QRect(480, 80, 211, 33)) 159 | 160 | # Search engines 161 | self.searchEngineSelector.addItem("Google") 162 | self.searchEngineSelector.addItem("Yahoo") 163 | self.searchEngineSelector.addItem("Bing") 164 | self.searchEngineSelector.addItem("DuckDuckGo") 165 | self.searchEngineSelector.currentTextChanged.connect(self.addDropDownItemToJson) 166 | 167 | if self.default_search_engine == "Google": 168 | self.searchEngineSelector.setCurrentIndex(0) 169 | elif self.default_search_engine == "Yahoo": 170 | self.searchEngineSelector.setCurrentIndex(1) 171 | elif self.default_search_engine == "Bing": 172 | self.searchEngineSelector.setCurrentIndex(2) 173 | elif self.default_search_engine == "DuckDuckGo": 174 | self.searchEngineSelector.setCurrentIndex(3) 175 | 176 | # Write to json 177 | 178 | def saveChangesToJson(self): # startup pg 179 | if len(self.startup_page.text()) > 0: 180 | self.settings_data["startupPage"] = self.startup_page.text() 181 | with open("settings.json", "w") as f: 182 | json.dump(self.settings_data, f, indent=2) 183 | 184 | if len(self.home_button_page.text()) > 0: 185 | self.settings_data["homeButtonPage"] = self.home_button_page.text() 186 | with open("settings.json", "w") as f: 187 | json.dump(self.settings_data, f, indent=2) 188 | 189 | if len(self.new_tab_page.text()) > 0: 190 | self.settings_data["newTabPage"] = self.new_tab_page.text() 191 | with open("settings.json", "w") as f: 192 | json.dump(self.settings_data, f, indent=2) 193 | 194 | def addDropDownItemToJson(self): 195 | self.settings_data[ 196 | "defaultSearchEngine" 197 | ] = self.searchEngineSelector.currentText() 198 | with open("settings.json", "w") as f: 199 | json.dump(self.settings_data, f, indent=2) 200 | 201 | def closeWindow(self): 202 | self.close() 203 | 204 | def retranslateUi(self): 205 | _translate = QtCore.QCoreApplication.translate 206 | self.label_2.setText(_translate("Form", "Settings")) 207 | self.label_3.setText(_translate("Form", "On startup")) 208 | self.save_settings.setText(_translate("Form", "Save settings")) 209 | self.label.setText(_translate("Form", "Default Search Engine")) 210 | self.label_4.setText( 211 | _translate("Form", "Choose what page to display on startup") 212 | ) 213 | self.label_5.setText( 214 | _translate("Form", "Default search engine used in the address bar") 215 | ) 216 | self.label_6.setText( 217 | _translate( 218 | "Form", "Choose what page to navigate when home button is pressed" 219 | ) 220 | ) 221 | self.label_7.setText(_translate("Form", "Home button")) 222 | self.label_8.setText( 223 | _translate("Form", "Choose what page to show when a new tab is opened") 224 | ) 225 | self.label_9.setText(_translate("Form", "New tab")) 226 | self.discard_changes.setText(_translate("Form", "Discard changes")) 227 | -------------------------------------------------------------------------------- /browser/widgets.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from PyQt5.QtWebEngineWidgets import QWebEnginePage 4 | from PyQt5.QtWidgets import QLabel, QLineEdit, QTabWidget 5 | from PyQt5.QtGui import QPixmap, QFont 6 | from PyQt5 import QtCore 7 | 8 | 9 | class AddressBar(QLineEdit): 10 | def __init__(self): 11 | super().__init__() 12 | self.setFocus() 13 | 14 | def mousePressEvent(self, e): 15 | self.selectAll() 16 | 17 | def initAddressBar(self): 18 | # Set the placeholder text 19 | self.setPlaceholderText("Search or enter web address") 20 | 21 | # Set focus to the address bar 22 | self.setFocus() 23 | with open(os.path.join("styles", "addr_bar.css")) as f: 24 | self.setStyleSheet(f.read()) 25 | 26 | 27 | class SSLIcon(QLabel): 28 | def __init__(self): 29 | super().__init__() 30 | self.InitSSLIcon() 31 | 32 | def InitSSLIcon(self): 33 | self.setObjectName("SSLIcon") 34 | icon = QPixmap(os.path.join("resources", "lock-icon.png")) 35 | self.setPixmap(icon) 36 | 37 | 38 | class Tabs(QTabWidget): 39 | def __init__(self): 40 | super().__init__() 41 | self.setDocumentMode(True) 42 | 43 | # Set the tabs closable 44 | self.setTabsClosable(True) 45 | 46 | # Set the tabs movable 47 | self.setMovable(True) 48 | 49 | # Add font family 50 | font = QFont("Segoe UI", 8) 51 | self.setFont(font) 52 | 53 | # Add some styles to the tabs 54 | with open( 55 | os.path.join("styles", "tab_style.css") 56 | ) as f: # Open tab_styles.css file 57 | self.setStyleSheet(f.read()) 58 | 59 | 60 | class customWebEnginePage(QWebEnginePage): 61 | def createWindow(self, _type): 62 | page = customWebEnginePage(self) 63 | page.urlChanged.connect(self.on_url_changed) 64 | return page 65 | 66 | @QtCore.pyqtSlot(QtCore.QUrl) 67 | def on_url_changed(self, url): 68 | page = self.sender() 69 | self.setUrl(url) 70 | page.deleteLater() 71 | -------------------------------------------------------------------------------- /browser_screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/browser_screenshot.jpg -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | ░██████╗██╗███╗░░░███╗██████╗░██╗░░░░░███████╗ ░██╗░░░░░░░██╗███████╗██████╗░ 4 | ██╔════╝██║████╗░████║██╔══██╗██║░░░░░██╔════╝ ░██║░░██╗░░██║██╔════╝██╔══██╗ 5 | ╚█████╗░██║██╔████╔██║██████╔╝██║░░░░░█████╗░░ ░╚██╗████╗██╔╝█████╗░░██████╦╝ 6 | ░╚═══██╗██║██║╚██╔╝██║██╔═══╝░██║░░░░░██╔══╝░░ ░░████╔═████║░██╔══╝░░██╔══██╗ 7 | ██████╔╝██║██║░╚═╝░██║██║░░░░░███████╗███████╗ ░░╚██╔╝░╚██╔╝░███████╗██████╦╝ 8 | ╚═════╝░╚═╝╚═╝░░░░░╚═╝╚═╝░░░░░╚══════╝╚══════╝ ░░░╚═╝░░░╚═╝░░╚══════╝╚═════╝░ 9 | 10 | ██████╗░██████╗░░█████╗░░██╗░░░░░░░██╗░██████╗███████╗██████╗░ 11 | ██╔══██╗██╔══██╗██╔══██╗░██║░░██╗░░██║██╔════╝██╔════╝██╔══██╗ 12 | ██████╦╝██████╔╝██║░░██║░╚██╗████╗██╔╝╚█████╗░█████╗░░██████╔╝ 13 | ██╔══██╗██╔══██╗██║░░██║░░████╔═████║░░╚═══██╗██╔══╝░░██╔══██╗ 14 | ██████╦╝██║░░██║╚█████╔╝░░╚██╔╝░╚██╔╝░██████╔╝███████╗██║░░██║ 15 | ╚═════╝░╚═╝░░╚═╝░╚════╝░░░░╚═╝░░░╚═╝░░╚═════╝░╚══════╝╚═╝░░╚═╝ 16 | 17 | Chromium based tabbed browser built with PyQt5 QWebEngineView 18 | Made by - Samin Sakur 19 | Github - https://github.com/saminsakur/PyQt5BrowserBuild/ 20 | """ 21 | 22 | from browser import main 23 | 24 | if __name__ == "__main__": 25 | main() 26 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyQt5~=5.15.5 2 | pyperclip~=1.8.2 -------------------------------------------------------------------------------- /resources/fonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/fonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /resources/icons/adobepdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/adobepdf.png -------------------------------------------------------------------------------- /resources/icons/app_window_ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/app_window_ios.png -------------------------------------------------------------------------------- /resources/icons/arrow-down-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/arrow-down-12.png -------------------------------------------------------------------------------- /resources/icons/closetab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/closetab.png -------------------------------------------------------------------------------- /resources/icons/closetabbutton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/closetabbutton.png -------------------------------------------------------------------------------- /resources/icons/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/cross.png -------------------------------------------------------------------------------- /resources/icons/globe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/globe.png -------------------------------------------------------------------------------- /resources/icons/history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/history.png -------------------------------------------------------------------------------- /resources/icons/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/home.png -------------------------------------------------------------------------------- /resources/icons/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/info.png -------------------------------------------------------------------------------- /resources/icons/info_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/info_24.png -------------------------------------------------------------------------------- /resources/icons/left-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/left-arrow.png -------------------------------------------------------------------------------- /resources/icons/lock_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/lock_icon.png -------------------------------------------------------------------------------- /resources/icons/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/more.png -------------------------------------------------------------------------------- /resources/icons/newtab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/newtab.png -------------------------------------------------------------------------------- /resources/icons/openclickhtml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/openclickhtml.png -------------------------------------------------------------------------------- /resources/icons/paste.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/paste.png -------------------------------------------------------------------------------- /resources/icons/printer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/printer.png -------------------------------------------------------------------------------- /resources/icons/printerprev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/printerprev.png -------------------------------------------------------------------------------- /resources/icons/question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/question.png -------------------------------------------------------------------------------- /resources/icons/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/refresh.png -------------------------------------------------------------------------------- /resources/icons/right-arrow-context-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/right-arrow-context-menu.png -------------------------------------------------------------------------------- /resources/icons/right-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/right-arrow.png -------------------------------------------------------------------------------- /resources/icons/save-disk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/save-disk.png -------------------------------------------------------------------------------- /resources/icons/security.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/security.png -------------------------------------------------------------------------------- /resources/icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/settings.png -------------------------------------------------------------------------------- /resources/icons/url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/url.png -------------------------------------------------------------------------------- /resources/icons/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/icons/warning.png -------------------------------------------------------------------------------- /resources/logos/browser.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/logos/browser.ico -------------------------------------------------------------------------------- /resources/logos/browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saminsakur/PyQt5BrowserBuild/37875b8a3f634acd8be58af6e8ade5e89d52f594/resources/logos/browser.png -------------------------------------------------------------------------------- /styles/about_style.css: -------------------------------------------------------------------------------- 1 | QPushButton { 2 | background-color: #2B5DD1; 3 | color: #FFFFFF; 4 | padding: 10px 30px; 5 | font: bold 14px; 6 | border-radius: 3px; 7 | } 8 | QPushButton:hover { 9 | background-color: #3769df; 10 | } 11 | -------------------------------------------------------------------------------- /styles/addr_bar.css: -------------------------------------------------------------------------------- 1 | QLineEdit{ 2 | font-family: \"Segoe UI\"; 3 | padding-top:4px; 4 | padding-left:8px; 5 | padding-bottom:4px; 6 | border:2px solid transparent; 7 | border-radius:6px; 8 | font-size:10pt; 9 | background-color: #ffffff; 10 | selection-background-color: #66c2ff; 11 | } 12 | 13 | QLineEdit:focus{ 14 | border-color:#3696ff; 15 | } 16 | 17 | QLineEdit:hover{ 18 | border-color:#d6d6d6 19 | } 20 | -------------------------------------------------------------------------------- /styles/history_style.css: -------------------------------------------------------------------------------- 1 | QLabel { 2 | padding-top: 10px; 3 | padding-left: 7px; 4 | } 5 | QPushButton#ClearButnHistory { 6 | border: 1px solid transparent; 7 | border-radius: 7px; 8 | border-color: #ccc; 9 | margin-top: 6px; 10 | margin-left: 80px; 11 | margin-right: 10px; 12 | padding: 5px 5px 5px 5px; 13 | font-size: 12pt; 14 | color: #000; 15 | background-color: transparent; 16 | } 17 | 18 | QPushButton#ClearButnHistory:hover { 19 | background-color: #2681f2; 20 | border-color: #dae0e5; 21 | color: #fff; 22 | } 23 | 24 | QPushButton#ClearButnHistory:pressed { 25 | background-color: #0c63ce; 26 | } 27 | QScrollBar:horizontal { 28 | height: 8px; 29 | } 30 | QScrollBar::handle:horizontal { 31 | background: gray; 32 | min-height: 5px; 33 | border: 1px solid gray; 34 | border-radius: 4px; 35 | } 36 | QScrollBar::left-arrow:horizontal { 37 | background: none; 38 | } 39 | QScrollBar::right-arrow:horizontal { 40 | background: none; 41 | } 42 | QScrollBar:vertical { 43 | background: transparent; 44 | width: 8px; 45 | margin: 0px 0px 0px 0px; 46 | } 47 | 48 | QScrollBar::handle:vertical { 49 | background: gray; 50 | min-width: 5px; 51 | border: 1px solid gray; 52 | border-radius: 4px; 53 | } 54 | QScrollBar::add-line:vertical { 55 | background: none; 56 | } 57 | QScrollBar::sub-line:vertical { 58 | background: none; 59 | } 60 | QListWidget::item { 61 | padding-top: 8px; 62 | padding-bottom: 8px; 63 | margin-top: 2px; 64 | margin-bottom: 2px; 65 | } 66 | 67 | QListWidget::item:hover { 68 | background-color: #dce9ef; 69 | } 70 | 71 | QListWidget { 72 | border: 1px solid transparent; 73 | border-top: 1px solid gray; 74 | /* padding-left:5px; 75 | padding-right:5px; */ 76 | } 77 | -------------------------------------------------------------------------------- /styles/settings_style.css: -------------------------------------------------------------------------------- 1 | QWidget{ 2 | background-color:#fefefe; 3 | } 4 | QPushButton#closeButn{ 5 | border: 1px solid transparent; 6 | border-radius: 3px; 7 | } 8 | QPushButton#closeButn:hover{ 9 | background-color:#d1d1d1; 10 | } 11 | QLineEdit, QComboBox{ 12 | border: 1px solid #ccc; 13 | border-radius: 5px; 14 | padding: 5px 5px; 15 | height: 60px; 16 | font-size: 12px; 17 | background-color: #fff; 18 | } 19 | QLineEdit:focus{ 20 | border-color: #2781F2; 21 | } 22 | QComboBox:on{ 23 | border-color: #2781F2; 24 | } 25 | QComboBox::down-arrow{ 26 | image: url(./resources/arrow-down-12.png); 27 | border : none; 28 | } 29 | QComboBox::drop-down{ 30 | border: none; 31 | } 32 | QPushButton#save_settings{ 33 | font-size: 10pt; 34 | padding: 5px; 35 | border: 1px solid darkgray; 36 | border-radius: 7px; 37 | background-color: #2781F2; 38 | color: #ffffff; 39 | } 40 | QPushButton#discard_changes{ 41 | font-size: 10pt; 42 | background: transparent; 43 | border: 1px solid #FF0000; 44 | border-radius: 7px; 45 | padding:5px; 46 | } 47 | QPushButton#save_settings:hover{ 48 | background-color: #3185eb; 49 | } 50 | QPushButton#save_settings:pressed{ 51 | background-color: #387cd1; 52 | } 53 | QPushButton#discard_changes:hover{ 54 | background-color: #E81123; 55 | color: #fff; 56 | } 57 | QPushButton#discard_changes:pressed{ 58 | background-color: #9B0B17; 59 | } 60 | QLabel#label_4, QLabel#label_5, QLabel#label_6, QLabel#label_8{ 61 | color: #606770; 62 | } 63 | -------------------------------------------------------------------------------- /styles/styles.css: -------------------------------------------------------------------------------- 1 | QPushButton#ContextMenuTriggerButn::menu-indicator { 2 | /* Hide context menu button dropdown icon */ 3 | image: none; 4 | } 5 | 6 | QToolBar { 7 | background-color: #edf4f7; 8 | } 9 | 10 | /* Style all contextmenus*/ 11 | 12 | Qmenu { 13 | background-color: fff; 14 | } 15 | 16 | /* Style right arrow of QMenu */ 17 | 18 | QMenu::right-arrow { 19 | image: url(resources/right-arrow-context-menu.png); 20 | height: 12px; 21 | width: 12px; 22 | } 23 | 24 | QMenu::item { 25 | /* Styling all context menus */ 26 | background-color: transparent; 27 | font-size: 10pt; 28 | padding-left: 10px; 29 | padding-right: 100px; 30 | padding-top: 5px; 31 | padding-bottom: 5px; 32 | width: 120px; 33 | } 34 | 35 | QMenu::item:selected { 36 | background-color: #dedeff; 37 | } 38 | 39 | /* 40 | The three dot menu 41 | */ 42 | 43 | QMenu#ContextMenu, 44 | QMenu#HelpMenu { 45 | background-color: #fdfdfd; 46 | border: 1px solid transparent; 47 | font-family: sans-serif; 48 | border-radius: 6px; 49 | } 50 | 51 | QMenu#ContextMenu::item, 52 | QMenu#HelpMenu::item { 53 | background-color: transparent; 54 | font-size: 10pt; 55 | padding-left: 40px; 56 | padding-right: 100px; 57 | padding-top: 8px; 58 | padding-bottom: 8px; 59 | width: 130px; 60 | } 61 | 62 | QMenu#ContextMenu::icon, 63 | QMenu#HelpMenu::icon { 64 | padding-left: 40px; 65 | } 66 | 67 | QMenu#ContextMenu::separator { 68 | height: 1px; 69 | background-color: gray; 70 | margin-left: 0%; 71 | margin-right: 0%; 72 | } 73 | 74 | QMenu#ContextMenu::item:selected, 75 | QMenu#HelpMenu::item:selected { 76 | background-color: #f2f2f2; 77 | } 78 | 79 | /* 80 | Styling of toolip 81 | */ 82 | 83 | QToolTip { 84 | background-color: #131c21; 85 | font-size: 10pt; 86 | opacity: 200; 87 | color: #f1f1f1; 88 | padding: 5px; 89 | border-width: 2px; 90 | border-style: solid; 91 | border-radius: 20px; 92 | border: 2px solid transparent; 93 | } 94 | 95 | /* SSLinfo label */ 96 | 97 | QLabel#SSLIcon { 98 | /* ssl icon */ 99 | border: 1px solid transparent; 100 | padding-left: 10px; 101 | padding-right: 10px; 102 | border-radius: 6px; 103 | width: 5px; 104 | height: 5px; 105 | } 106 | 107 | /* Button styling */ 108 | 109 | QPushButton#ContextMenuTriggerButn { 110 | /* Context menu button */ 111 | border: 1px solid transparent; 112 | padding: 10px; 113 | border-radius: 16px; 114 | width: 10px; 115 | height: 10px; 116 | background-color: none; 117 | margin-left: 5px; 118 | margin-right: 5px; 119 | } 120 | 121 | QPushButton#back_btn { 122 | border: 1px solid transparent; 123 | padding: 10px; 124 | border-radius: 7px; 125 | width: 10px; 126 | height: 10px; 127 | background-color: none; 128 | } 129 | 130 | QPushButton#forward_butn { 131 | border: 1px solid transparent; 132 | padding: 10px; 133 | border-radius: 7px; 134 | width: 10px; 135 | height: 10px; 136 | background-color: none; 137 | } 138 | 139 | QPushButton#reload_butn { 140 | border: 1px solid transparent; 141 | padding: 10px; 142 | border-radius: 7px; 143 | width: 10px; 144 | height: 10px; 145 | background-color: none; 146 | } 147 | 148 | QPushButton#home_button { 149 | border: 1px solid transparent; 150 | padding: 10px; 151 | border-radius: 7px; 152 | width: 10px; 153 | height: 10px; 154 | background-color: none; 155 | } 156 | 157 | QPushButton#stop_butn { 158 | border: 1px solid transparent; 159 | padding: 10px; 160 | border-radius: 7px; 161 | width: 10px; 162 | height: 10px; 163 | background-color: none; 164 | } 165 | 166 | /* 167 | * after hover 168 | */ 169 | 170 | QPushButton#stop_butn:hover { 171 | background-color: #dce9ef; 172 | } 173 | 174 | QPushButton#back_btn:hover { 175 | background-color: #dce9ef; 176 | } 177 | 178 | QPushButton#forward_butn:hover { 179 | background-color: #dce9ef; 180 | } 181 | 182 | QPushButton#reload_butn:hover { 183 | background-color: #dce9ef; 184 | } 185 | 186 | QPushButton#home_button:hover { 187 | background-color: #dce9ef; 188 | } 189 | 190 | QPushButton#ContextMenuTriggerButn:hover { 191 | background-color: #dce9ef; 192 | } 193 | 194 | /* 195 | * after pressed 196 | */ 197 | 198 | QPushButton#stop_butn:pressed { 199 | background-color: #cadfe7; 200 | } 201 | 202 | QPushButton#back_btn:pressed { 203 | background-color: #cadfe7; 204 | } 205 | 206 | QPushButton#forward_butn:pressed { 207 | background-color: #cadfe7; 208 | } 209 | 210 | QPushButton#reload_butn:pressed { 211 | background-color: #cadfe7; 212 | } 213 | 214 | QPushButton#home_button:pressed { 215 | background-color: #cadfe7; 216 | } 217 | 218 | QPushButton#ContextMenuTriggerButn:pressed { 219 | background-color: #cadfe7; 220 | } 221 | -------------------------------------------------------------------------------- /styles/tab_style.css: -------------------------------------------------------------------------------- 1 | QTabBar{ 2 | background-color:#2E385C; 3 | } 4 | 5 | QTabBar::tab { 6 | background-color: none; 7 | color: #fff; 8 | padding: 5px 0px; 9 | padding-left: 4px; 10 | border-top-left-radius: 6px; 11 | border-top-right-radius: 6px; 12 | padding-right:80px; 13 | max-width:80px; 14 | text-align: left; 15 | } 16 | 17 | QTabBar::tab:!selected{ 18 | background-color: transparent; 19 | } 20 | 21 | QTabBar::close-button { /* style the tab close button */ 22 | image: url(./resources/icons/closetabbutton.png); 23 | subcontrol-position: right; 24 | border: 1px solid transparent; 25 | border-radius:3px; 26 | } 27 | 28 | QTabBar::close-button:hover{ /* close button hover */ 29 | background-color: #3B2E53; 30 | } 31 | 32 | QTabBar::tab:!selected:hover{ 33 | background-color:#222348; 34 | } 35 | 36 | QTabBar::tab:selected{ /* selected tabs */ 37 | background-color: #170733; 38 | } 39 | --------------------------------------------------------------------------------