from PyQt5.QtCore import QUrl, QSize, Qt
from PyQt5.QtWidgets import QAction, QMenu, QLabel, QGridLayout, QDesktopWidget, QSpinBox, QRadioButton, \
QPushButton, QToolBar, QApplication, QMainWindow, QWidget, QTabWidget, QLineEdit
from PyQt5.QtGui import QFont, QIcon
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage, QWebEngineSettings, QWebEngineProfile
import sys, os, linecache, requests, keyboard
serverip = "http://aava.dy.fi"
serverip_nohttp = "aava.dy.fi"
offlinepage = "file:///data/html/index.html"
allowed = [serverip, serverip_nohttp, offlinepage]
user_agent = "mikset_sina_tanssi,_hyva_mies?_tanssinhan_minakin." #custom user agent, can't have ä or ö
currenturl = None #stores the current url for the copyright window
radiobtnstate = None
window_file = open('data/conf.txt') #get window size from data file
home_file = open('data/home.txt') #get homepage from data file
linecache.clearcache()
window_width = float(linecache.getline('data/conf.txt', 1))
window_height = float(linecache.getline('data/conf.txt', 2))
if str(linecache.getline('data/home.txt', 1)).startswith("online"): #checks homepage preference, "online" or "offline", doesn't work with == "online"
emptytab = QUrl.fromUserInput(serverip)
else:
emptytab = QUrl.fromUserInput(offlinepage)
window_file.close(), home_file.close()
syntaxerror = """
Syntax error
Syntax Error
You need to begin your address with "aava/".
"""
offline = """
Connection failed
Cannot connect to page
Sorry, we couldn't connect you to your page right now.
Aava servers might be offline, or your Internet connection might be down. Please try again later.
"""
class WebEnginePage(QWebEnginePage):
def acceptNavigationRequest(self, url, _type, isMainFrame):
urlstring = url.toString()
if (_type == QWebEnginePage.NavigationTypeLinkClicked and url.host() not in allowed and urlstring.startswith("chrome-extension://") == False): #allow extensions for pdf files
#self.MainWindow.tabs.setHtml(syntaxerror)
#self.setHtml(syntaxerror)
return False
if (_type == QWebEnginePage.NavigationTypeTyped and url.host() not in allowed and urlstring.startswith("chrome-extension://") == False and urlstring.startswith(serverip) == False and urlstring.startswith(serverip_nohttp) == False):
#serverip comes from open in new tab
#self.MainWindow.tabs.setHtml(syntaxerror)
#self.setHtml(syntaxerror)
return False
if (_type == QWebEnginePage.NavigationTypeReload and url.host() not in allowed and urlstring.startswith("chrome-extension://") == False and urlstring.startswith(serverip) == False and urlstring.startswith(serverip_nohttp) == False):
return False
if (_type == QWebEnginePage.NavigationTypeRedirect and url.host() not in allowed and urlstring.startswith("chrome-extension://") == False and urlstring.startswith(serverip) == False and urlstring.startswith(serverip_nohttp) == False):
return False
return super().acceptNavigationRequest(url, _type, isMainFrame)
class Tabs(QTabWidget):
def __init__(self):
super().__init__()
class Browser(QWebEngineView):
def __init__(self, MainWindow):
super().__init__()
self.MainWindow = MainWindow
def contextMenuEvent(self, event):
if self.page().contextMenuData().selectedText() and self.page().contextMenuData().linkUrl().isEmpty() == True: #test if there's text selected but no link clicked
self.menus2 = QMenu() #if a link wasn't clicked, menu 2 is used
self.menus2.setStyleSheet("background-color:rgb(209,222,235); QMenu::item::hover {text-color:rgb(30,30,30);}")
action1 = QAction("Copy text", self)
action2 = QAction("View copyright info", self)
self.menus2.addAction(action1)
self.menus2.addAction(action2)
self.menus2.popup(event.globalPos())
action1.triggered.connect(self.copy_text)
action2.triggered.connect(self.open_copyright_window)
elif self.page().contextMenuData().mediaType() == 1 and self.page().contextMenuData().linkUrl().isEmpty() == True: #test if an image was clicked (img = 1, video = 2, audio = 3, file = 5)
self.menus = QMenu()
imgurl = self.page().contextMenuData().mediaUrl()
self.menus.setStyleSheet("background-color:rgb(209,222,235); QMenu::item::hover {text-color:rgb(30,30,30);}")
action1 = QAction("Save image to disk", self)
action2 = QAction("Copy image url", self)
self.menus.addAction(action1)
self.menus.addAction(action2)
self.menus.popup(event.globalPos())
action1.triggered.connect(lambda imgsave: self.saveimg(imgurl))
action2.triggered.connect(lambda imageurl: self.copy_imgurl(imgurl))
elif self.page().contextMenuData().mediaType() == 1 and self.page().contextMenuData().linkUrl().isEmpty() == False: #image + link
self.menus = QMenu()
imgurl = self.page().contextMenuData().mediaUrl()
urli = self.page().contextMenuData().linkUrl()
self.menus.setStyleSheet("background-color:rgb(209,222,235); QMenu::item::hover {text-color:rgb(30,30,30);}")
action1 = QAction("Save image to disk", self)
action2 = QAction("Open link in new tab", self)
action3 = QAction("Copy url", self)
action4 = QAction("Copy image url", self)
self.menus.addAction(action1)
self.menus.addAction(action2)
self.menus.addAction(action3)
self.menus.addAction(action4)
self.menus.popup(event.globalPos())
browser = Browser(self)
action1.triggered.connect(lambda imgsave: self.saveimg(imgurl))
action2.triggered.connect(lambda urlaus:self.MainWindow.add_new_tab(browser, urli))
action3.triggered.connect(self.copy_url)
action4.triggered.connect(lambda imageurl: self.copy_imgurl(imgurl))
elif self.page().contextMenuData().mediaType() == 3: #test if an audio file was clicked
self.menus = QMenu()
imgurl = self.page().contextMenuData().mediaUrl()
self.menus.setStyleSheet("background-color:rgb(209,222,235); QMenu::item::hover {text-color:rgb(30,30,30);}")
action1 = QAction("Save audio to disk", self)
self.menus.addAction(action1)
self.menus.popup(event.globalPos())
action1.triggered.connect(lambda imgsave: self.saveimg(imgurl))
elif self.page().contextMenuData().isContentEditable() == True: #paste into input field
self.menus = QMenu()
self.menus.setStyleSheet("background-color:rgb(209,222,235); QMenu::item::hover {text-color:rgb(30,30,30);}")
action1 = QAction("Paste text", self)
self.menus.addAction(action1)
self.menus.popup(event.globalPos())
action1.triggered.connect(self.paste_into_field)
elif self.page().contextMenuData().linkUrl().isEmpty() == False and not self.page().contextMenuData().selectedText(): #test if a link was right-clicked
self.menus = QMenu()
urli = self.page().contextMenuData().linkUrl()
urlistring = self.page().contextMenuData().linkUrl().toString()
self.menus.setStyleSheet("background-color:rgb(209,222,235); QMenu::item::hover {text-color:rgb(30,30,30);}")
if urlistring.endswith(".pdf"): #saving pdf files
action1 = QAction("Open link in new tab", self)
action3 = QAction("Save .pdf file to disk", self)
action2 = QAction("Copy url", self)
action4 = QAction("View copyright info", self)
self.menus.addAction(action1)
self.menus.addAction(action2)
self.menus.addAction(action3)
self.menus.addAction(action4)
action1.triggered.connect(lambda urlaus: self.MainWindow.add_new_tab(browser, urli))
action2.triggered.connect(self.copy_url)
imgurl = self.page().contextMenuData().linkUrl()
action3.triggered.connect(lambda imgsave: self.saveimg(imgurl))
action4.triggered.connect(self.open_copyright_window)
elif urlistring.endswith(".zip"): #saving zip files
action1 = QAction("Open link in new tab", self)
action3 = QAction("Save .zip file to disk", self)
action2 = QAction("Copy url", self)
action4 = QAction("View copyright info", self)
self.menus.addAction(action1)
self.menus.addAction(action2)
self.menus.addAction(action3)
self.menus.addAction(action4)
action1.triggered.connect(lambda urlaus: self.MainWindow.add_new_tab(browser, urli))
action2.triggered.connect(self.copy_url)
imgurl = self.page().contextMenuData().linkUrl()
action3.triggered.connect(lambda imgsave: self.saveimg(imgurl))
action4.triggered.connect(self.open_copyright_window)
else:
action1 = QAction("Open link in new tab", self)
action2 = QAction("Copy url", self)
action4 = QAction("View copyright info", self)
self.menus.addAction(action1)
self.menus.addAction(action2)
self.menus.addAction(action4)
action1.triggered.connect(lambda urlaus: self.MainWindow.add_new_tab(browser, urli))
action2.triggered.connect(self.copy_url)
action4.triggered.connect(self.open_copyright_window)
self.menus.popup(event.globalPos())
browser = Browser(self)
elif self.page().contextMenuData().linkUrl().isEmpty() == False and self.page().contextMenuData().selectedText(): #test if a link was right-clicked and there's text selected
self.menus = QMenu()
self.menus.setStyleSheet("background-color:rgb(209,222,235); QMenu::item::hover {text-color:rgb(30,30,30);}")
action1 = QAction("Open link in new tab", self)
action2 = QAction("Copy url", self)
action3 = QAction("Copy text", self)
action4 = QAction("View copyright info", self)
self.menus.addAction(action1)
self.menus.addAction(action2)
self.menus.addAction(action3)
self.menus.addAction(action4)
self.menus.popup(event.globalPos())
action1.triggered.connect(lambda urlaus:self.MainWindow.add_new_tab(browser, urli))
action2.triggered.connect(self.copy_url)
action3.triggered.connect(self.copy_text)
action4.triggered.connect(self.open_copyright_window)
browser = Browser(self)
urli = self.page().contextMenuData().linkUrl()
else:
self.menus3 = QMenu() #if a link wasn't clicked, menu 2 is used
self.menus3.setStyleSheet("background-color:rgb(209,222,235); QMenu::item::hover {text-color:rgb(30,30,30);}")
action1 = QAction("View copyright info", self)
self.menus3.addAction(action1)
self.menus3.popup(event.globalPos())
action1.triggered.connect(self.open_copyright_window)
def saveimg(self, imgurl):
fileurl = imgurl.toString() #needs to be string
headers = {'User-Agent': user_agent} #requests needs this too
response = requests.get(fileurl, headers=headers)
filename = fileurl.split("/")[-1] #get filename from url
dlfolder = "downloads/"
file_path = os.path.join(dlfolder, filename)
open(file_path, "wb").write(response.content)
def paste_into_field(self):
keyboard.press_and_release("ctrl+v") #replace this with a better solution
def copy_imgurl(self, imgurl):
urli = imgurl.toString()
layered = urli.replace(serverip, "aava")
clipboard = QApplication.clipboard()
clipboard.setText(layered)
def copy_url(self):
urli = self.page().contextMenuData().linkUrl().toString()
layered = urli.replace(serverip, "aava")
clipboard = QApplication.clipboard()
clipboard.setText(layered)
def copy_text(self):
self.triggerPageAction(QWebEnginePage.Copy)
def open_copyright_window(self):
self.copyright_window = CopyrightDialog() #this must have "self", otherwise the new window instance is destroyed immediately
class Urlbar(QLineEdit):
def __init__(self, MainWindow):
super().__init__()
self.MainWindow = MainWindow
def contextMenuEvent(self, event):
if self.hasSelectedText() == True:
self.menus = QMenu()
self.menus.setStyleSheet("background-color:rgb(209,222,235); QMenu::item::hover {text-color:rgb(30,30,30);}")
action1 = QAction("Copy", self)
action2 = QAction("Cut", self)
action3 = QAction("Paste and Go", self)
action4 = QAction("Paste", self)
self.menus.addAction(action1)
self.menus.addAction(action2)
self.menus.addAction(action3)
self.menus.addAction(action4)
self.menus.popup(event.globalPos())
action1.triggered.connect(self.copy_text)
action2.triggered.connect(self.cut_action)
action3.triggered.connect(self.paste_go)
action4.triggered.connect(self.just_paste)
else:
self.menus2 = QMenu()
self.menus2.setStyleSheet("background-color:rgb(209,222,235); QMenu::item::hover {text-color:rgb(30,30,30);}")
action1 = QAction("Paste and Go", self)
action2 = QAction("Paste", self)
self.menus2.addAction(action1)
self.menus2.addAction(action2)
self.menus2.popup(event.globalPos())
action1.triggered.connect(self.paste_go)
#browser = Browser(self)
#action2.triggered.connect(lambda urlaus:self.MainWindow.add_new_tab(browser, urli))
action2.triggered.connect(self.just_paste)
def paste_go(self):
self.clear()
self.paste()
pastedurl = self.text()
if pastedurl.startswith(serverip_nohttp):
addhttp = "http://" + pastedurl
self.MainWindow.tabs.currentWidget().load(QUrl(addhttp))
elif pastedurl.startswith("aava/"):
layered = pastedurl.replace("aava", serverip) #backwards layering: when user types aava addresses it loads serverip
self.MainWindow.tabs.currentWidget().load(QUrl(layered))
else:
self.MainWindow.tabs.currentWidget().load(QUrl(pastedurl))
def copy_text(self):
self.copy() #copies selected text to clipboard (QLineEdit handles copy and paste more simply than qwebengine)
def cut_action(self):
self.cut()
def just_paste(self):
self.paste()
class Searchbar(QLineEdit):
def __init__(self, MainWindow):
super().__init__()
self.MainWindow = MainWindow
class SettingsDialog(QMainWindow):
instance = None #instance is stored in this variable
def center(self):
qrectangle = self.frameGeometry()
centerpoint = QDesktopWidget().availableGeometry().center()
qrectangle.moveCenter(centerpoint)
def delete_instance(self):
if self.__class__.instance is not None:
try:
self.__class__.instance.deleteLater()
except Exception as e:
pass
def __init__(self):
self.delete_instance()
self.__class__.instance = self
super(SettingsDialog, self).__init__()
self.resize(750, 400)
self.center()
self.setStyleSheet("background-color:rgb(187,213,238);")
self.setMaximumWidth(750)
self.setMaximumHeight(400)
file_object = open('data/conf.txt') #these need to be opened again
file_object2 = open('data/home.txt')
linecache.clearcache()
window_width = float(linecache.getline('data/conf.txt', 1))
window_height = float(linecache.getline('data/conf.txt', 2))
self.setWindowTitle("Settings")
layout = QGridLayout()
text = QLabel("Set the percentage of how much Aava Browser will occupy of your display\n" #text labels 1-4
"when it starts up. Over 95% in either field will result in full screen:")
text.setStyleSheet("background-color:rgb(214,230,245); border-radius: 6px; padding: 8px;")
font = text.font()
font.setPointSize(10)
text.setFont(font)
layout.addWidget(text, 0, 0, 2, 2) #last two numbers are row span and column span
text4 = QLabel("Height (%):")
text4.setStyleSheet("background-color:rgb(214,230,245); border-radius: 6px; padding: 8px;")
font = text4.font()
font.setPointSize(9)
text4.setFont(font)
layout.addWidget(text4, 0, 2, 1, 2)
text3 = QLabel("Width (%):")
text3.setStyleSheet("background-color:rgb(214,230,245); border-radius: 6px; padding: 8px;")
font = text3.font()
font.setPointSize(9)
text3.setFont(font)
layout.addWidget(text3, 1, 2, 1, 2)
text2 = QLabel("Set empty tab page:")
text2.setStyleSheet("background-color:rgb(214,230,245); border-radius: 6px; padding: 8px;")
font = text2.font()
font.setPointSize(10)
text2.setFont(font)
layout.addWidget(text2, 4, 0, 2, 2)
self.spinBox = QSpinBox() #spinboxes
self.spinBox.setStyleSheet("background-color:rgb(214,230,245); border:0; max-width: 50px; max-height: 30px;")
self.spinBox.setMaximum(100)
self.spinBox.setProperty("value", window_width)
self.spinBox2 = QSpinBox()
self.spinBox2.setStyleSheet("background-color:rgb(214,230,245); border:0; max-width: 50px; max-height: 30px;")
self.spinBox2.setMaximum(100)
self.spinBox2.setProperty("value", window_height)
layout.addWidget(self.spinBox, 0, 3)
layout.addWidget(self.spinBox2, 1, 3)
self.radio1 = QRadioButton("Aava homepage (Online)") #radiobuttons
self.radio1.setStyleSheet("background-color:rgb(214,230,245); border-radius: 6px; padding: 8px;")
self.radio1.toggled.connect(lambda: self.radiobtn_state(self.radio1))
layout.addWidget(self.radio1, 4,2,1,2)
self.radio2 = QRadioButton("Empty page (Offline)")
self.radio2.setStyleSheet("background-color:rgb(214,230,245); border-radius: 6px; padding: 8px;")
self.radio2.toggled.connect(lambda: self.radiobtn_state(self.radio2))
layout.addWidget(self.radio2, 5,2,1,2)
checkhome = str(linecache.getline('data/home.txt', 1)) #check previous homepage settings
if checkhome.startswith("online"):
self.radio1.setChecked(True)
self.radio2.setChecked(False)
elif checkhome.startswith("offline"):
self.radio1.setChecked(False)
self.radio2.setChecked(True)
text5 = QLabel("Clear http cache, session, and browsing history:") #clear http cache and cookies, row 5
text5.setStyleSheet("QLabel {background-color:rgb(214,230,245); border-radius: 6px; padding: 8px;}")
font = text5.font()
font.setPointSize(10)
text5.setFont(font)
layout.addWidget(text5, 6, 0, 1, 2)
self.cachebtn = QPushButton()
self.cachebtn.setText("Clear cache")
self.cachebtn.setStyleSheet("QPushButton {background:rgb(109,121,160); border:0; color: #fff; height: 30px; font-size: 12px; cursor: pointer;}"
"QPushButton:hover {background:rgb(109,141,190);}"
"QPushButton:pressed {background-color:rgb(130,130,225);}")
self.cachebtn.clicked.connect(self.clear_cache)
layout.addWidget(self.cachebtn, 6, 2, 1, 2)
self.okbutton = QPushButton() #ok button and cancel button, row 6
self.okbutton.setText("Save changes")
self.okbutton.setStyleSheet("QPushButton {background:rgb(109,121,160); border:0; color: #fff; height: 30px; font-size: 12px; cursor: pointer;}"
"QPushButton:hover {background:rgb(109,141,190);}"
"QPushButton:pressed {background-color:rgb(130,130,225);}")
self.okbutton.clicked.connect(self.okbtn_pressed)
layout.addWidget(self.okbutton, 7, 0, 2, 1)
self.cancelbutton = QPushButton()
self.cancelbutton.setStyleSheet("QPushButton {background:rgb(109,121,160); border:0; color: #fff; height: 30px; font-size: 12px; cursor: pointer;}"
"QPushButton:hover {background-color:rgb(109,141,190);}"
"QPushButton:pressed {background-color:rgb(130,130,225);}")
self.cancelbutton.setGeometry(100,100,100,100)
self.cancelbutton.setText("Cancel")
self.cancelbutton.clicked.connect(self.cancelbtn_pressed)
layout.addWidget(self.cancelbutton, 7, 2, 2, 2)
text6 = QLabel("Aava Browser 0.3 - Released 4.12.2022") #browser version
text6.setStyleSheet("QLabel {background-color:rgb(214,230,245); border-radius: 6px; padding: 8px;}")
layout.addWidget(text6, 9, 0, 1, 4)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
self.setWindowIcon(QIcon("data/img/settingsicon.png"))
self.show()
file_object.close()
file_object2.close()
def radiobtn_state(self, b):
global radiobtnstate #these text have to be identical to the labels above, pretty dumb
if b.text() == "Aava homepage (Online)":
if b.isChecked() == True:
radiobtnstate = 1
if b.text() == "Empty page (Offline)":
if b.isChecked() == True:
radiobtnstate = 0
def okbtn_pressed(self):
widthvalue = str(self.spinBox.value()) #these have to be in string or else crash
heightvalue = str(self.spinBox2.value())
L = [widthvalue, heightvalue]
linecache.clearcache()
conftxt = open('data/conf.txt', 'w')
hometxt = open('data/home.txt', 'w')
conftxt.writelines(i + "\n" for i in L)
if radiobtnstate == 1: #writes either "online" or "offline" to conf.txt
hometxt.write("online")
if radiobtnstate == 0:
hometxt.write("offline")
conftxt.close()
hometxt.close()
self.close()
def cancelbtn_pressed(self):
self.close()
def clear_cache(self):
browser = Browser(self)
profile = QWebEngineProfile("aavastorage", browser)
browser.page().profile().clearHttpCache()
browser.page().profile().clearAllVisitedLinks()
QWebEngineProfile.cookieStore(profile).deleteAllCookies()
class CopyrightDialog(QMainWindow):
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
def __init__(self):
super().__init__()
self.resize(400, 300)
self.center()
self.setMaximumHeight(600)
self.setMaximumWidth(800)
self.setWindowTitle("Page copyright information")
self.browser = QWebEngineView()
if currenturl.startswith("http") == True: #checks if a url is a live site or a non-online html page
urlParts = currenturl.split("/") #get a version of url that doesn't have "index.php" etc. in the end
urlWithoutFilename = "/".join(urlParts[:-1]) + "/"
copyurl = (urlWithoutFilename + "/copy.html")
self.browser.setPage(WebEnginePage(self)) #set a custom webenginepage to block clicking on outside urls
self.browser.load(QUrl(copyurl))
self.setCentralWidget(self.browser)
self.browser.setContextMenuPolicy(Qt.NoContextMenu) #we don't want a context menu in the copyright window
self.setWindowIcon(QIcon("data/img/ico64.ico"))
self.show()
return
class MainWindow(QMainWindow):
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().screenGeometry().center()
qr.moveCenter(cp)
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setMinimumHeight(480)
self.setMinimumWidth(600)
if window_width > 95 or window_height > 95: #resize window relative to screen size, unless user wishes fullscreen (this function prefers integers)
self.showMaximized()
else:
available_geometry = app.desktop().screenGeometry()
self.resize(int(available_geometry.width() * window_width/100), int(available_geometry.height() * window_height/100))
self.center()
self.setStyleSheet("background-image: url(data/img/backg.png) no-repeat;")
self.tabs = Tabs()
self.setCentralWidget(self.tabs) #setting tabs as the central widget of the window
self.tabs.setDocumentMode(True)
self.tabs.setTabsClosable(True)
self.tabs.setMovable(True)
self.tabs.currentChanged.connect(self.current_tab_changed)
self.tabs.tabCloseRequested.connect(self.close_tab)
self.tabs.setStyleSheet("QTabBar {background-image: url(data/img/tabbg.png); min-height:22px; padding:0px;}"
"QTabBar::tab {left:0px; border:1px solid black; padding:2px; min-width:100px;}"
#if you give negative margins, you get overlapping tabs
"QTabBar::tab:selected {text-align:left; border:1px solid; color:black; margin-left:0px; margin-right:0px; background-image:url(data/img/tabselected.png); "
"max-width:200px; min-height:25; border-top-left-radius:4px; border-top-right-radius:4px;}"
"QTabBar::tab:!selected {border:1px solid; color:gray; background-image:url(data/img/tabnotselected.png); max-width:200px; border-top-left-radius:4px; border-top-right-radius:4px;}"
"QTabBar::tab:!selected:hover {color:black; background-image:url(data/img/tabhover.png);}"
"QTabBar::tab:top:!selected {margin-top: 3px;}" #unselected tab is smaller
"QTabBar::tab:only-one {margin-left: 0px;}" #first and last tab margins so they don't touch window edges
"QTabBar::tab:top:!selected {border-bottom-color:none;}" #no bottom border for tabs
"QTabBar::tab:top:selected {border-bottom:0px; border-bottom-color:none;}")
toolbar = QToolBar() #toolbar, we'll disable the context menu it has by default
QToolBar.setContextMenuPolicy(self, Qt.NoContextMenu)
toolbar.setIconSize(QSize(50, 44))
toolbar.setMovable(False)
toolbar.setStyleSheet("padding:1px; height:100px; spacing:15px; border:0px;")
self.addToolBar(toolbar)
#toolbar buttons and urlbar
cornerwidget = QPushButton(self.tabs) #new tab button
self.tabs.setCornerWidget(cornerwidget, Qt.TopLeftCorner)
cornerwidget.setStyleSheet("QPushButton:!hover {outline:none; margin-bottom:-1px; border-image:url(data/img/plusbtn.png); min-width:30px; min-height:30px; background:transparent;}"
"QPushButton:hover {outline:none; border-image:url(data/img/plusbtn-h.png);}")
spacer = QPushButton() #spacer (just a blank .png)
toolbar.addWidget(spacer)
spacer.setStyleSheet("QPushButton {outline:none; border-image:url(data/img/spacer.png); max-height:60px; max-width:10px; background:transparent;}")
backbtn = QPushButton() #back button
toolbar.addWidget(backbtn)
backbtn.setStyleSheet("QPushButton:!hover {outline:none; border-image:url(data/img/back6054.png); min-width:44px; max-height:40px; background:transparent;}" #outline:none removes focus rectangle
"QPushButton:hover {outline:none; border-image:url(data/img/back6054-h.png);}")
forwardbtn = QPushButton() #forward button
toolbar.addWidget(forwardbtn)
forwardbtn.setStyleSheet("QPushButton:!hover {outline:none; border-image:url(data/img/for6054.png); min-width:44; max-height:40px; background:transparent;}"
"QPushButton:hover {outline:none; border-image:url(data/img/for6054-h.png);}")
refreshbtn = QPushButton() #refresh button
toolbar.addWidget(refreshbtn)
refreshbtn.setStyleSheet("QPushButton:!hover {outline:none; border-image:url(data/img/refresh6054.png); min-width:44; max-height:40px; background:transparent;}"
"QPushButton:hover {outline:none; border-image:url(data/img/refresh6054-h.png); background:transparent;}")
homebtn = QPushButton() #home button
toolbar.addWidget(homebtn)
homebtn.setStyleSheet("QPushButton:!hover {outline:none; border-image:url(data/img/home6054.png); min-width:44; max-height:40px; background:transparent;}"
"QPushButton:hover {outline:none; border-image:url(data/img/home6054-h.png); background:transparent;}")
browsebtn = QPushButton() #browse button
toolbar.addWidget(browsebtn)
browsebtn.setStyleSheet("QPushButton:!hover {outline:none; border-image:url(data/img/globe6054.png); min-width:44; max-height:40px; background:transparent;}"
"QPushButton:hover {outline:none; border-image:url(data/img/globe6054-h.png); background:transparent;}")
self.urlbar = Urlbar(self) #address bar
self.urlbar.setStyleSheet("QLineEdit:!focus {background-image:url(data/img/urlbar.png); border:2px solid; border-color:rgb(239, 245, 250); border-radius:5px; height:25px;}"
"QLineEdit:focus {background-image:url(data/img/urlbar.png); border:2px solid; border-color:rgb(121, 193, 206); border-radius:5px; height:25px;}")
font = QFont()
font.setFamily("Arial")
font.setPointSize(11)
font.setBold(False)
self.urlbar.setFont(font)
toolbar.addWidget(self.urlbar)
self.urlbar.returnPressed.connect(self.go_to_url) #press enter to load url
profilebtn = QPushButton() #profile button
toolbar.addWidget(profilebtn)
profilebtn.setStyleSheet("QPushButton:!hover {outline:none; border-image:url(data/img/profile6054.png); min-width:44; max-height:40px; background:transparent;}"
"QPushButton:hover {outline:none; border-image:url(data/img/profile6054-h.png); background:transparent;}")
ulbtn = QPushButton() #upload button
toolbar.addWidget(ulbtn)
ulbtn.setStyleSheet("QPushButton:!hover {outline:none; border-image:url(data/img/ulbutton6054.png); min-width:44; max-height:40px; background:transparent;}"
"QPushButton:hover {outline:none; border-image:url(data/img/ulbutton6054-h.png); background:transparent;}")
dlbtn = QPushButton() #download button
toolbar.addWidget(dlbtn)
dlbtn.setStyleSheet("QPushButton:!hover {outline:none; border-image:url(data/img/folder6054.png); min-width:44; max-height:40px; background:transparent;}"
"QPushButton:hover {outline:none; border-image:url(data/img/folder6054-h.png); background:transparent;}")
searchbtn = QPushButton() #search button
toolbar.addWidget(searchbtn)
searchbtn.setStyleSheet("QPushButton:!hover {outline:none; border-image:url(data/img/search.png); min-width:44; max-height:40px; background:transparent;}"
"QPushButton:hover {outline:none; border-image:url(data/img/search.png); background:transparent;}")
settingsbtn = QPushButton() #settings button
toolbar.addWidget(settingsbtn)
settingsbtn.setStyleSheet("QPushButton:!hover {outline:none; border-image:url(data/img/settings6054.png); min-width:44; max-height:40px; background:transparent;}"
"QPushButton:hover {outline:none; border-image:url(data/img/settings6054-h.png); background:transparent;}")
spacer2 = QPushButton() #spacer2 (just a blank .png)
toolbar.addWidget(spacer2)
spacer2.setStyleSheet("QPushButton {outline:none; border-image:url(data/img/spacer.png); max-height:52px; max-width:10px; background:transparent;}")
cornerwidget.clicked.connect(self.add_new_tab) #button signals
backbtn.clicked.connect(self.back)
forwardbtn.clicked.connect(self.forward)
refreshbtn.clicked.connect(self.refresh)
homebtn.clicked.connect(self.home)
browsebtn.clicked.connect(self.browse)
profilebtn.clicked.connect(self.profile)
ulbtn.clicked.connect(self.ul)
dlbtn.clicked.connect(self.dl)
settingsbtn.clicked.connect(self.settings)
searchbtn.clicked.connect(self.search)
self.menuBar().setNativeMenuBar(False) #disable native menubar on Mac devices
browser = Browser(self) #start by adding a new tab
self.add_new_tab(browser, emptytab)
self.setWindowIcon(QIcon("data/img/ico64.ico"))
self.show()
def add_new_tab(self, browser, urli=None):
index = self.tabs.count()
browser = Browser(self)
browser.setPage(WebEnginePage(self)) #custom webenginepage to block outside urls
browser.page().profile().setHttpUserAgent(user_agent) #send custom user-agent to the server
settings = browser.settings()
settings.setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, True)
settings.setAttribute(QWebEngineSettings.WebAttribute.PdfViewerEnabled, True)
self.tabs.addTab(browser, "") #2nd argument is title
self.tabs.setCurrentIndex(index)
if urli != None: #gotta set the url in the urlbar, crashes with an empty urli
urltext = urli.toString()
layeredurltext = urltext.replace(serverip, "aava")
self.urlbar.setText(layeredurltext)
if urli == None:
browser.setUrl(emptytab)
urltext = emptytab.toString()
else:
browser.setUrl(QUrl(urli))
urltext = urli.toString()
if urltext == str(offlinepage): #offline page is read from disk
self.urlbar.setText("") #sets "" in the urlbar to signify empty tab
browser.loadFinished.connect(lambda tabtitles: self.on_load_finished(browser))
browser.urlChanged.connect(lambda url: self.update_urlbar_and_window(url, browser))
global currenturl #gives the copyright window functionality the current address
currenturl = urltext
def on_load_finished(self, browser):
loadedpage = browser
i = self.tabs.indexOf(loadedpage)
title = browser.page().title()
#if len(title) == 0: #checks if loaded title is 0 characters to detect offline situations
#self.tabs.currentWidget().setHtml(offline)
layered = title.replace(serverip_nohttp, "aava") #this needs the nohttp version for some reason
self.setWindowTitle(layered) #set window title
self.tabs.setTabText(i, layered) #set tab title
def go_to_url(self):
url = QUrl(self.urlbar.text()).toString()
if url.startswith(serverip_nohttp):
addhttp = "http://" + url
self.tabs.currentWidget().load(QUrl(addhttp))
#elif url.startswith("aava/university/aavapedia"): #mediawiki has some redirect policy
#layered = url.replace("aava/", "http://aavaone.dy.fi/")
#self.tabs.currentWidget().load(QUrl(layered))
elif url.startswith("aava/"):
layered = url.replace("aava/", serverip + "/") #backwards layering: when user types aava addresses it loads serverip
self.tabs.currentWidget().load(QUrl(layered))
else:
self.tabs.currentWidget().load(QUrl(url))
def update_urlbar_and_window(self, url, browser):
if browser != self.tabs.currentWidget():
return
global currenturl #this is for the copyright window
currenturl = url.toString()
windowtitle = self.tabs.currentWidget().page().title()
layeredtitle = windowtitle.replace(serverip_nohttp, "aava") #this needs the nohttp version for some reason
self.setWindowTitle(layeredtitle)
urltext = url.toString()
if urltext == offlinepage: #again, layering for empty tab page
self.urlbar.setText("")
elif urltext.startswith("data:"):
text = self.urlbar.text()
self.urlbar.setText(text)
elif urltext.startswith("chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html?"):
plugin = urltext.replace("chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html?", "")
self.urlbar.setText(plugin)
else:
layered = urltext.replace(serverip, "aava")
self.urlbar.setText(layered)
self.urlbar.setCursorPosition(0) #brings cursor back to the beginning
def current_tab_changed(self):
url = self.tabs.currentWidget().url()
self.update_urlbar_and_window(url, self.tabs.currentWidget())
def close_tab(self, index):
if self.tabs.count() > 1:
self.tabs.removeTab(index)
return
def back(self): #button events start here
self.tabs.currentWidget().back()
def forward(self):
self.tabs.currentWidget().forward()
def refresh(self):
self.tabs.currentWidget().reload()
def home(self):
self.tabs.currentWidget().setUrl(QUrl(serverip))
def browse(self):
self.tabs.currentWidget().load(QUrl(serverip + "/browse"))
def profile(self):
self.tabs.currentWidget().load(QUrl(serverip + "/my/"))
def ul(self):
self.tabs.currentWidget().load(QUrl(serverip + "/upload"))
def dl(self):
path = "downloads"
path = os.path.realpath(path)
os.startfile(path)
def search(self):
searchtext = self.urlbar.text()
self.tabs.currentWidget().findText(searchtext)
def settings(self):
self.second_window = SettingsDialog() #this must have "self", otherwise the new window instance is destroyed immediately
def closeEvent(self):
sys.exit(0)
app = QApplication(sys.argv)
app.setApplicationName("Aava Browser")
app.setOrganizationName("Aava Network")
window = MainWindow()
app.exec_()