Skip to content

Instantly share code, notes, and snippets.

@lukestanley
Last active September 26, 2022 06:29
Show Gist options
  • Save lukestanley/20509edaf7eb952c3b493df16aa6dbe2 to your computer and use it in GitHub Desktop.
Save lukestanley/20509edaf7eb952c3b493df16aa6dbe2 to your computer and use it in GitHub Desktop.
A tabbed browser with thumbnails of each site in the tab bar on Linux using PySide2 with basic easylist adblocking
import sys
import time
import json
import os
import hashlib
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import (
QApplication,
QTabWidget,
QLineEdit,
QWidget,
QVBoxLayout,
QHBoxLayout,
QPushButton,
)
from PySide2 import QtCore, QtWidgets, QtWebEngineCore, QtWebEngineWidgets
from adblockparser import AdblockRules
import urllib.request
if not os.path.exists("easylist.txt"):
urllib.request.urlretrieve("https://easylist.to/easylist/easylist.txt", "easylist.txt")
with open("easylist.txt") as f:
raw_rules = f.readlines()
rules = AdblockRules(raw_rules)
if not os.path.exists(os.path.expanduser("~/.browser/")):
os.mkdir(os.path.expanduser("~/.browser/"))
if os.path.exists("tabs.json"):
with open("tabs.json", "r") as f:
tabList = json.load(f)
else:
tabList = [
"https://www.reddit.com",
"https://www.youtube.com",
"https://www.duckduckgo.com",
]
app = QApplication(sys.argv)
window = QWidget()
window.setGeometry(0, 0, app.desktop().availableGeometry().width(), app.desktop().availableGeometry().height())
layout = QVBoxLayout()
window.setLayout(layout)
locationBar = QWidget()
locationBarLayout = QHBoxLayout()
locationBar.setLayout(locationBarLayout)
tabs = QTabWidget()
tabs.setTabsClosable(True)
tabs.setMovable(True)
tabs.setTabPosition(QTabWidget.North)
tabs.setTabShape(QTabWidget.Rounded)
tabs.setStyleSheet(
"""QTabBar::tab { height: 140px; width: 256px; }; QTabBar::tab:selected { background: #fff; }; QTabBar::tab:selected { color: #000; }; QTabWidget { background: #fff; }; QTabWidget::pane { border: 0px; }"""
)
tabs.setIconSize(QSize(128, 128))
newTabButton = QPushButton("+")
newTabButton.setStyleSheet(
"""QPushButton { background: #fff; color: #000; }; QPushButton:focus { background: #fff; color: #000; }; QPushButton:hover { background: #fff; color: #000; }; QPushButton:pressed { background: #fff; color: #000; }"""
)
newTabButton.setFont(QFont("Arial", 20))
newTabButton.setFixedHeight(40)
newTabButton.setFixedWidth(40)
location = QLineEdit()
location.setStyleSheet(
"""QLineEdit { background: #fff; color: #000; }; QLineEdit:focus { background: #fff; color: #000; }"""
)
location.setFont(QFont("Arial", 20))
location.setFixedHeight(40)
location.setText("")
def createWindow(web):
return create_new_tab("about:blank")
class WebEngineUrlRequestInterceptor(QtWebEngineCore.QWebEngineUrlRequestInterceptor):
def interceptRequest(self, info):
url = info.requestUrl().toString()
if rules.should_block(url):
print("blocks", url, info.resourceType())
info.block(True)
class WebEnginePage(QtWebEngineWidgets.QWebEnginePage):
def __init__(self, parent=None):
super(WebEnginePage, self).__init__(parent)
self.profile().setUrlRequestInterceptor(WebEngineUrlRequestInterceptor())
class WebEngineView(QtWebEngineWidgets.QWebEngineView):
def __init__(self, parent=None, url=None):
super(WebEngineView, self).__init__(parent)
self.setPage(WebEnginePage(self))
self.interceptor = WebEngineUrlRequestInterceptor
if url:
self.load(url)
def webView():
view = WebEngineView()
return view
def create_new_tab(url, manual=False):
web = webView()
web.createWindow = createWindow
web.load(QUrl(url))
tabs.addTab(web, "New Tab")
tabs.setCurrentWidget(web)
update_tab_icon(web, do=False)
web.loadFinished.connect(lambda: update_tab_icon(web))
if manual:
location.setFocus()
location.selectAll()
else:
location.clearFocus()
update_location(web)
update_tab_list()
return web
def update_tab_list():
tabList = []
for i in range(tabs.count()):
tabList.append(tabs.widget(i).url().toString())
with open("tabs.json", "w") as f:
json.dump(tabList, f)
def on_location_returnPressed():
web = tabs.currentWidget()
aLocation = location.text()
if "http" not in aLocation:
aLocation = "https://" + aLocation
web.load(QUrl(aLocation))
location.clearFocus()
update_tab_list()
location.returnPressed.connect(on_location_returnPressed)
def on_newTabButton_clicked():
create_new_tab("about:blank", manual=True)
newTabButton.clicked.connect(on_newTabButton_clicked)
def on_tabCloseRequested(index):
widget = tabs.widget(index)
widget.deleteLater()
tabs.removeTab(index)
update_tab_list()
tabs.tabCloseRequested.connect(on_tabCloseRequested)
def on_tabBarClicked(index):
web = tabs.currentWidget()
location.setText(web.url().toString())
location.clearFocus()
tabs.tabBarClicked.connect(on_tabBarClicked)
import urllib.parse
def tidy_url(url):
parsed_url = urllib.parse.urlparse(url)
hostname = parsed_url.hostname
if hostname[:4] == 'www.':
return hostname[4:]
return hostname
def take_screenshot(web):
size = web.contentsRect()
image = QImage(size.width(), size.height(), QImage.Format_ARGB32)
painter = QPainter(image)
web.render(painter, QPoint(), QRegion(QRect(QPoint(), web.size())))
painter.end()
return image.scaled(256, 256, Qt.KeepAspectRatio, Qt.SmoothTransformation).copy(
0, 0, 256, 256
)
def sha1_hash_hex(a):
return hashlib.sha1(a.encode("utf-8")).hexdigest()
def update_tab_icon(web, do=True):
url = web.url().toString()
thumbnailPath = os.path.expanduser("~/.browser/" + sha1_hash_hex(url) + ".png")
if (tabs.currentWidget() == web) and do:
image = take_screenshot(web)
painter = QPainter(image)
painter.setPen(QColor(255, 255, 255))
painter.setFont(QFont("Arial", 20))
painter.drawText(
image.rect(), Qt.AlignBottom, web.title() + "\n" + tidy_url(url)
)
painter.end()
image.save(thumbnailPath)
tabs.setTabIcon(tabs.indexOf(web), QIcon(QPixmap.fromImage(image)))
tabs.setTabText(tabs.indexOf(web), "")
update_tab_list()
else:
if os.path.exists(thumbnailPath):
tabs.setTabIcon(tabs.indexOf(web), QIcon(QPixmap.fromImage(QImage(thumbnailPath))))
tabs.setTabText(tabs.indexOf(web), "")
if do:
QTimer.singleShot(1000, lambda: update_tab_icon(web))
update_location(web)
def update_location(web):
if tabs.currentWidget() == web and not location.hasFocus():
location.setText(web.url().toString())
QTimer.singleShot(200, lambda: update_location(web))
for tab in tabList:
create_new_tab(tab)
locationBarLayout.addWidget(location)
locationBarLayout.addWidget(newTabButton)
layout.addWidget(tabs)
layout.addWidget(locationBar)
layout.addWidget(tabs)
window.show()
sys.exit(app.exec_())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment