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_()