├── .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 | 
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 |
--------------------------------------------------------------------------------