├── 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 | Made With Python 3.9.1 5 | 6 | ![GitHub top language](https://img.shields.io/github/languages/top/tech35/Python-Browser-Version-2) 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 | ![ ](https://github.com/tech35/Python-Browser-Version-2/blob/main/screenshot-browser-tabbed.jpg?raw=true) 17 | 18 | 19 | 20 | [![Gitter](https://badges.gitter.im/tech35/community.svg)](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 | --------------------------------------------------------------------------------