├── requirements.txt
├── images
├── home.png
├── cross.png
├── printer.png
├── arrow-000.png
├── arrow-180.png
├── lifebuoy.png
├── lock-ssl.png
├── question.png
├── cross-circle.png
├── disk--arrow.png
├── disk--pencil.png
├── lock-nossl.png
├── ma-icon-128.png
├── ma-icon-256.png
├── ma-icon-64.png
├── ui-tab--plus.png
└── arrow-circle-315.png
├── screenshot-browser-tabbed.jpg
├── README.md
└── pythonbrowser.py
/requirements.txt:
--------------------------------------------------------------------------------
1 | PyQt5>=5.6
2 | sip
3 |
--------------------------------------------------------------------------------
/images/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/home.png
--------------------------------------------------------------------------------
/images/cross.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/cross.png
--------------------------------------------------------------------------------
/images/printer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/printer.png
--------------------------------------------------------------------------------
/images/arrow-000.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/arrow-000.png
--------------------------------------------------------------------------------
/images/arrow-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/arrow-180.png
--------------------------------------------------------------------------------
/images/lifebuoy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/lifebuoy.png
--------------------------------------------------------------------------------
/images/lock-ssl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/lock-ssl.png
--------------------------------------------------------------------------------
/images/question.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/question.png
--------------------------------------------------------------------------------
/images/cross-circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/cross-circle.png
--------------------------------------------------------------------------------
/images/disk--arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/disk--arrow.png
--------------------------------------------------------------------------------
/images/disk--pencil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/disk--pencil.png
--------------------------------------------------------------------------------
/images/lock-nossl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/lock-nossl.png
--------------------------------------------------------------------------------
/images/ma-icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/ma-icon-128.png
--------------------------------------------------------------------------------
/images/ma-icon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/ma-icon-256.png
--------------------------------------------------------------------------------
/images/ma-icon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/ma-icon-64.png
--------------------------------------------------------------------------------
/images/ui-tab--plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/ui-tab--plus.png
--------------------------------------------------------------------------------
/images/arrow-circle-315.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/images/arrow-circle-315.png
--------------------------------------------------------------------------------
/screenshot-browser-tabbed.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech35/Python-Browser-Version-2/HEAD/screenshot-browser-tabbed.jpg
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Python-Browser
2 | A web browser made in Python!
3 |
4 |
5 |
6 | 
7 |
8 | Made through: PyQt5
9 |
10 | To install Python 3.9.1 [click here.](https://www.python.org/downloads/)
11 |
12 | To check how to install PyQt5 please visit [PyPI](https://pypi.org/)
13 |
14 | Screenshot:
15 |
16 | 
17 |
18 |
19 |
20 | [](https://gitter.im/tech35/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
21 |
22 | Consider following if you like cool code like this.
23 |
24 |
25 | Have a great day and stay safe.
26 |
--------------------------------------------------------------------------------
/pythonbrowser.py:
--------------------------------------------------------------------------------
1 | from PyQt5.QtCore import *
2 | from PyQt5.QtWidgets import *
3 | from PyQt5.QtGui import *
4 | from PyQt5.QtWebEngineWidgets import *
5 | from PyQt5.QtPrintSupport import *
6 |
7 | import os
8 | import sys
9 |
10 |
11 | class AboutDialog(QDialog):
12 | def __init__(self, *args, **kwargs):
13 | super(AboutDialog, self).__init__(*args, **kwargs)
14 |
15 | QBtn = QDialogButtonBox.Ok # No cancel
16 | self.buttonBox = QDialogButtonBox(QBtn)
17 | self.buttonBox.accepted.connect(self.accept)
18 | self.buttonBox.rejected.connect(self.reject)
19 |
20 | layout = QVBoxLayout()
21 |
22 | title = QLabel("Tech35's Web Browser")
23 | font = title.font()
24 | font.setPointSize(20)
25 | title.setFont(font)
26 |
27 | layout.addWidget(title)
28 |
29 | logo = QLabel()
30 | logo.setPixmap(QPixmap(os.path.join('images', 'ma-icon-128.png')))
31 | layout.addWidget(logo)
32 |
33 | layout.addWidget(QLabel("Version 2.0"))
34 | layout.addWidget(QLabel("Copyright Tech35"))
35 |
36 | for i in range(0, layout.count()):
37 | layout.itemAt(i).setAlignment(Qt.AlignHCenter)
38 |
39 | layout.addWidget(self.buttonBox)
40 |
41 | self.setLayout(layout)
42 |
43 |
44 | class MainWindow(QMainWindow):
45 | def __init__(self, *args, **kwargs):
46 | super(MainWindow, self).__init__(*args, **kwargs)
47 |
48 | self.tabs = QTabWidget()
49 | self.tabs.setDocumentMode(True)
50 | self.tabs.tabBarDoubleClicked.connect(self.tab_open_doubleclick)
51 | self.tabs.currentChanged.connect(self.current_tab_changed)
52 | self.tabs.setTabsClosable(True)
53 | self.tabs.tabCloseRequested.connect(self.close_current_tab)
54 |
55 | self.setCentralWidget(self.tabs)
56 |
57 | self.status = QStatusBar()
58 | self.setStatusBar(self.status)
59 |
60 | navtb = QToolBar("Navigation")
61 | navtb.setIconSize(QSize(16, 16))
62 | self.addToolBar(navtb)
63 |
64 | back_btn = QAction(QIcon(os.path.join('images', 'arrow-180.png')), "Back", self)
65 | back_btn.setStatusTip("Back to previous page")
66 | back_btn.triggered.connect(lambda: self.tabs.currentWidget().back())
67 | navtb.addAction(back_btn)
68 |
69 | next_btn = QAction(QIcon(os.path.join('images', 'arrow-000.png')), "Forward", self)
70 | next_btn.setStatusTip("Forward to next page")
71 | next_btn.triggered.connect(lambda: self.tabs.currentWidget().forward())
72 | navtb.addAction(next_btn)
73 |
74 | reload_btn = QAction(QIcon(os.path.join('images', 'arrow-circle-315.png')), "Reload", self)
75 | reload_btn.setStatusTip("Reload page")
76 | reload_btn.triggered.connect(lambda: self.tabs.currentWidget().reload())
77 | navtb.addAction(reload_btn)
78 |
79 | home_btn = QAction(QIcon(os.path.join('images', 'home.png')), "Home", self)
80 | home_btn.setStatusTip("Go home")
81 | home_btn.triggered.connect(self.navigate_home)
82 | navtb.addAction(home_btn)
83 |
84 | navtb.addSeparator()
85 |
86 | self.httpsicon = QLabel() # Yes, really!
87 | self.httpsicon.setPixmap(QPixmap(os.path.join('images', 'lock-nossl.png')))
88 | navtb.addWidget(self.httpsicon)
89 |
90 | self.urlbar = QLineEdit()
91 | self.urlbar.returnPressed.connect(self.navigate_to_url)
92 | navtb.addWidget(self.urlbar)
93 |
94 | stop_btn = QAction(QIcon(os.path.join('images', 'cross-circle.png')), "Stop", self)
95 | stop_btn.setStatusTip("Stop loading current page")
96 | stop_btn.triggered.connect(lambda: self.tabs.currentWidget().stop())
97 | navtb.addAction(stop_btn)
98 |
99 | # Uncomment to disable native menubar on Mac
100 | # self.menuBar().setNativeMenuBar(False)
101 |
102 | file_menu = self.menuBar().addMenu("&File")
103 |
104 | new_tab_action = QAction(QIcon(os.path.join('images', 'ui-tab--plus.png')), "New Tab", self)
105 | new_tab_action.setStatusTip("Open a new tab")
106 | new_tab_action.triggered.connect(lambda _: self.add_new_tab())
107 | file_menu.addAction(new_tab_action)
108 |
109 | open_file_action = QAction(QIcon(os.path.join('images', 'disk--arrow.png')), "Open file...", self)
110 | open_file_action.setStatusTip("Open from file")
111 | open_file_action.triggered.connect(self.open_file)
112 | file_menu.addAction(open_file_action)
113 |
114 | save_file_action = QAction(QIcon(os.path.join('images', 'disk--pencil.png')), "Save Page As...", self)
115 | save_file_action.setStatusTip("Save current page to file")
116 | save_file_action.triggered.connect(self.save_file)
117 | file_menu.addAction(save_file_action)
118 |
119 | print_action = QAction(QIcon(os.path.join('images', 'printer.png')), "Print...", self)
120 | print_action.setStatusTip("Print current page")
121 | print_action.triggered.connect(self.print_page)
122 | file_menu.addAction(print_action)
123 |
124 | help_menu = self.menuBar().addMenu("&Help")
125 |
126 | about_action = QAction(QIcon(os.path.join('images', 'question.png')), "About Tech35's Web Browser", self)
127 | about_action.setStatusTip("Find out more about Tech35's Web Browser") # Hungry!
128 | about_action.triggered.connect(self.about)
129 | help_menu.addAction(about_action)
130 |
131 | navigate_mozarella_action = QAction(QIcon(os.path.join('images', 'lifebuoy.png')),
132 | "Tech35's Web Browser Homepage", self)
133 | navigate_mozarella_action.setStatusTip("Go to Tech35's Web Browser Homepage")
134 | navigate_mozarella_action.triggered.connect(self.navigate_mozarella)
135 | help_menu.addAction(navigate_mozarella_action)
136 |
137 | self.add_new_tab(QUrl('https://github.com/tech35'), 'Homepage')
138 |
139 | self.show()
140 |
141 | self.setWindowTitle("Tech35's Web Browser")
142 | self.setWindowIcon(QIcon(os.path.join('images', 'ma-icon-64.png')))
143 |
144 | def add_new_tab(self, qurl=None, label="Blank"):
145 |
146 | if qurl is None:
147 | qurl = QUrl('')
148 |
149 | browser = QWebEngineView()
150 | browser.setUrl(qurl)
151 | i = self.tabs.addTab(browser, label)
152 |
153 | self.tabs.setCurrentIndex(i)
154 |
155 | # More difficult! We only want to update the url when it's from the
156 | # correct tab
157 | browser.urlChanged.connect(lambda qurl, browser=browser:
158 | self.update_urlbar(qurl, browser))
159 |
160 | browser.loadFinished.connect(lambda _, i=i, browser=browser:
161 | self.tabs.setTabText(i, browser.page().title()))
162 |
163 | def tab_open_doubleclick(self, i):
164 | if i == -1: # No tab under the click
165 | self.add_new_tab()
166 |
167 | def current_tab_changed(self, i):
168 | qurl = self.tabs.currentWidget().url()
169 | self.update_urlbar(qurl, self.tabs.currentWidget())
170 | self.update_title(self.tabs.currentWidget())
171 |
172 | def close_current_tab(self, i):
173 | if self.tabs.count() < 2:
174 | return
175 |
176 | self.tabs.removeTab(i)
177 |
178 | def update_title(self, browser):
179 | if browser != self.tabs.currentWidget():
180 | # If this signal is not from the current tab, ignore
181 | return
182 |
183 | title = self.tabs.currentWidget().page().title()
184 | self.setWindowTitle("%s - Tech35's Web Browser" % title)
185 |
186 | def navigate_mozarella(self):
187 | self.tabs.currentWidget().setUrl(QUrl("https://github.com/tech35"))
188 |
189 | def about(self):
190 | dlg = AboutDialog()
191 | dlg.exec_()
192 |
193 | def open_file(self):
194 | filename, _ = QFileDialog.getOpenFileName(self, "Open file", "",
195 | "Hypertext Markup Language (*.htm *.html);;"
196 | "All files (*.*)")
197 |
198 | if filename:
199 | with open(filename, 'r') as f:
200 | html = f.read()
201 |
202 | self.tabs.currentWidget().setHtml(html)
203 | self.urlbar.setText(filename)
204 |
205 | def save_file(self):
206 | filename, _ = QFileDialog.getSaveFileName(self, "Save Page As", "",
207 | "Hypertext Markup Language (*.htm *html);;"
208 | "All files (*.*)")
209 |
210 | if filename:
211 | html = self.tabs.currentWidget().page().toHtml()
212 | with open(filename, 'w') as f:
213 | f.write(html.encode('utf8'))
214 |
215 | def print_page(self):
216 | dlg = QPrintPreviewDialog()
217 | dlg.paintRequested.connect(self.browser.print_)
218 | dlg.exec_()
219 |
220 | def navigate_home(self):
221 | self.tabs.currentWidget().setUrl(QUrl("https://github.com/tech35"))
222 |
223 | def navigate_to_url(self): # Does not receive the Url
224 | q = QUrl(self.urlbar.text())
225 | if q.scheme() == "":
226 | q.setScheme("http")
227 |
228 | self.tabs.currentWidget().setUrl(q)
229 |
230 | def update_urlbar(self, q, browser=None):
231 |
232 | if browser != self.tabs.currentWidget():
233 | # If this signal is not from the current tab, ignore
234 | return
235 |
236 | if q.scheme() == 'https':
237 | # Secure padlock icon
238 | self.httpsicon.setPixmap(QPixmap(os.path.join('images', 'lock-ssl.png')))
239 |
240 | else:
241 | # Insecure padlock icon
242 | self.httpsicon.setPixmap(QPixmap(os.path.join('images', 'lock-nossl.png')))
243 |
244 | self.urlbar.setText(q.toString())
245 | self.urlbar.setCursorPosition(0)
246 |
247 |
248 | app = QApplication(sys.argv)
249 | app.setApplicationName("Tech35's Web Browser")
250 | app.setOrganizationName("Tech35")
251 | app.setOrganizationDomain("Google.com")
252 |
253 | window = MainWindow()
254 |
255 | app.exec_()
256 |
--------------------------------------------------------------------------------