Qt6 Web Browser je desktop aplikacija zasnovana na Python-u i PyQt6 biblioteci, koja omogućava korisnicima jednostavno i efikasno pregledanje internetskog sadržaja. Koristeći Qt WebEngine, koji je temeljen na Chromium-u, ovaj browser pruža brz i siguran browsing iskustvo. PyPI
U svetu modernih web pregledača često se susrećemo sa softverom koji je ili preopterećen funkcijama ili nedovoljno fleksibilan za integraciju sa sopstvenim alatima. Qweb Browser predstavlja elegantno rešenje za korisnike koji žele brz, prilagodljiv i extensibilan web preglednik, napravljen u Pythonu i PyQt6.
Ključne karakteristike Qweb Browsera
- Moderni Qt6 tab sistem
Qweb koristiQTabWidgetza rad sa više kartica. Svaka kartica funkcioniše kao potpuno nezavisni web preglednik (QWebEngineView), što omogućava jednostavno upravljanje, otvaranje novih tabova dvostrukim klikom i zatvaranje kartica po potrebi. - Napredni toolbar i navigacija
Preglednik ima standardne navigacione dugmiće: nazad, napred, osveži i početna stranica. Pored toga, toolbar uključuje dugmad za pokretanje HTML editora i Code Extractor-a, što omogućava direktnu integraciju sa Python alatima. - Pokretanje eksternih alata iz preglednika
- HTML Editor (
htmleditor.py) – Omogućava korisnicima da brzo otvore trenutnu stranicu u sopstvenom HTML editoru i rade izmene lokalno ili u realnom vremenu. - Code Extractor (
wordpress2code.py) – Omogućava izdvajanje sadržaja stranice i pripremu za objavljivanje ili integraciju u WordPress, bez potrebe za ručnim kopiranjem koda.
- HTML Editor (
- Pametan kontekst meni
Desni klik na elemente stranice omogućava:- Kopiranje i lepljenje teksta
- Otvaranje linkova i slika u novim tabovima ili prozorima
- Preuzimanje fajlova i slika direktno sa stranice
- Pregled izvornog koda stranice
- Direktno pokretanje HTML editora i Code Extractor-a
- Automatsko preuzimanje fajlova
Qweb automatski prepoznaje fajlove sa ekstenzijama poput.zip,.pdf,.exe,.mp3,.mp4i nudi njihovo preuzimanje putem standardnog dijaloga za čuvanje fajla. - Integracija Google Translate-a
Preglednik omogućava instant prevođenje stranice na srpski jezik putem Google Translate-a, što je korisno za brzu lokalizaciju sadržaja. - Pregled i čuvanje izvornog koda
Svaka stranica se može pregledati u formatu čistog HTML-a u posebnom prozoru, sa mogućnošću čuvanja za dalje uređivanje ili analizu.
Tehnički detalji
Qweb je razvijen u Pythonu koristeći PyQt6 i PyQt6 WebEngine. Instalacija zavisnosti je jednostavna:
python3 -m pip install pyqt6 pyqt6-webengine requests
Preglednik koristi QWebEngineUrlRequestInterceptor za kontrolu i blokiranje zahteva, što omogućava nadzor nad preuzimanjima i zaštitu od neželjenog sadržaja.
Qweb Browser je idealan za programere, web dizajnere i napredne korisnike koji žele minimalistički ali moćan alat za surfovanje webom. Njegova integracija sa eksternim Python alatima, kao što su HTML Editor i Code Extractor, čini ga jedinstvenim alatom za produktivnost i web razvoj..
Ako imate dodatne predloge ili želite da doprinosite razvoju ove aplikacije, slobodno se obratite. Vaš doprinos je dobrodošao!
U isti folderu u koji ste iskopirali Qweb.py treba iskopirati i
https://abel.rs/htmleditor-za-linux-alat-za-uredjivanje-pregled-i-konverziju-wordpress-sadrzaja-u-docx-i-pdf/
https://abel.rs/wordpress-enlighterjs-code-extractor/
Da bi Qweb.py bio potpuno funkcionalan.
Komplet programa možete preuzeti na adresi:
Instalacija zavisnosti:
python3 -m pip install pyqt6 pyqt6-webengine requests
Programski kod za Qweb.py:
# Qweb.py
# Instalacija zavisnosti: python3 -m pip install pyqt6 pyqt6-webengine requests
import sys
import requests
from PyQt6.QtCore import QUrl, QSize, Qt
from PyQt6.QtGui import QAction, QIcon, QKeyEvent
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QToolBar, QLineEdit,
QTabWidget, QMenu, QStatusBar, QPlainTextEdit, QFileDialog, QStyle
)
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWebEngineCore import QWebEngineUrlRequestInterceptor
HOME_URL = "https://abel.rs/zx/"
class RequestInterceptor(QWebEngineUrlRequestInterceptor):
def __init__(self, parent=None, browser=None):
super().__init__(parent)
self.browser = browser
self.downloaded_urls = set()
def interceptRequest(self, info):
url = info.requestUrl().toString()
# Ako je url za preuzimanje fajla
if self.is_downloadable(url):
if url not in self.downloaded_urls:
self.downloaded_urls.add(url)
self.download_file(url)
info.block(True)
else:
# Dozvoli ostale zahteve
info.block(False)
def is_downloadable(self, url):
download_extensions = [
'.zip', '.rar', '.exe', '.pdf', '.docx', '.xlsx',
'.tar', '.gz', '.7z', '.mp3', '.mp4', '.avi', '.mov', '.dmg', '.iso', '.apk'
]
return any(url.lower().endswith(ext) for ext in download_extensions)
def download_file(self, url):
try:
response = requests.get(url, stream=True)
if response.status_code == 200:
filename = url.split('/')[-1]
save_path, _ = QFileDialog.getSaveFileName(self.browser, "Save File As", filename)
if save_path:
with open(save_path, 'wb') as f:
for chunk in response.iter_content(1024):
f.write(chunk)
self.browser.statusBar().showMessage(f"Preuzeto: {save_path}", 3000)
except Exception as e:
self.browser.statusBar().showMessage(f"Greška pri preuzimanju: {e}", 5000)
class Browser(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Qt6 Web Browser")
self.setGeometry(100, 100, 1200, 800)
self.source_windows = []
# TAB SISTEM
self.tabs = QTabWidget()
self.tabs.setDocumentMode(True)
self.tabs.tabBarDoubleClicked.connect(self.add_new_tab)
self.tabs.currentChanged.connect(self.current_tab_changed)
self.tabs.setTabsClosable(True)
self.tabs.tabCloseRequested.connect(self.close_current_tab)
self.setCentralWidget(self.tabs)
# TOOLBAR
self.init_toolbar()
# Dodaj početnu karticu
self.add_new_tab(QUrl(HOME_URL), "Home")
# Postavljanje interceptora za trenutnu karticu
self.set_interceptor_for(self.current_browser())
def init_toolbar(self):
navtb = QToolBar("Navigation")
size = self.style().pixelMetric(QStyle.PixelMetric.PM_ToolBarIconSize)
navtb.setIconSize(QSize(size, size))
self.addToolBar(navtb)
# Navigaciona dugmad
back_btn = QAction(QIcon.fromTheme("go-previous"), "Back", self)
back_btn.triggered.connect(lambda: self.current_browser().back())
navtb.addAction(back_btn)
next_btn = QAction(QIcon.fromTheme("go-next"), "Forward", self)
next_btn.triggered.connect(lambda: self.current_browser().forward())
navtb.addAction(next_btn)
reload_btn = QAction(QIcon.fromTheme("view-refresh"), "Reload", self)
reload_btn.triggered.connect(lambda: self.current_browser().reload())
navtb.addAction(reload_btn)
home_btn = QAction(QIcon.fromTheme("go-home"), "Home", self)
home_btn.triggered.connect(self.navigate_home)
navtb.addAction(home_btn)
new_tab_btn = QAction(QIcon.fromTheme("tab-new"), "New Tab", self)
new_tab_btn.triggered.connect(lambda: self.add_new_tab(QUrl(HOME_URL), "New Tab"))
navtb.addAction(new_tab_btn)
# Dodatne akcije
html_editor_action = QAction(QIcon.fromTheme("accessories-text-editor"), "Open HTML Editor", self)
html_editor_action.triggered.connect(lambda: self.open_html_editor(self.current_browser()))
navtb.addAction(html_editor_action)
code_extractor_action = QAction(QIcon.fromTheme("system-run"), "Code Extractor", self)
code_extractor_action.triggered.connect(lambda: self.export_to_wordpress(self.current_browser()))
navtb.addAction(code_extractor_action)
translate_action = QAction(QIcon.fromTheme("applications-internet"), "Prevedi stranicu", self)
translate_action.triggered.connect(lambda: self.translate_page(self.current_browser()))
navtb.addAction(translate_action)
# URL bar
self.urlbar = QLineEdit()
self.urlbar.returnPressed.connect(self.navigate_to_url)
navtb.addWidget(self.urlbar)
# Status bar
self.status = QStatusBar()
self.setStatusBar(self.status)
# ----------------- TAB & BROWSER -----------------
def add_new_tab(self, qurl=None, label="New Tab"):
browser = QWebEngineView()
if qurl is None:
qurl = QUrl(HOME_URL)
browser.setUrl(qurl)
self.add_new_tab_browser(browser, label)
# Postavljanje interceptora za novu karticu
self.set_interceptor_for(browser)
def add_new_tab_browser(self, browser, label="New Tab"):
i = self.tabs.addTab(browser, label)
self.tabs.setCurrentIndex(i)
browser.urlChanged.connect(lambda q, b=browser: self.update_urlbar(q, b))
browser.loadFinished.connect(lambda _, i=i, b=browser: self.tabs.setTabText(i, b.page().title()))
browser.createWindow = self.handle_new_window
# Povezivanje kontekst menija
browser.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
browser.customContextMenuRequested.connect(lambda pos, b=browser: self.show_context_menu(pos, b))
# ----------------- PODEŠAVANJE INTERCEPTORA -----------------
def set_interceptor_for(self, browser):
profile = browser.page().profile()
interceptor = RequestInterceptor(browser=browser)
profile.setUrlRequestInterceptor(interceptor)
# ----------------- HANDLE NEW WINDOW -----------------
def handle_new_window(self, _type):
new_browser = QWebEngineView()
self.add_new_tab_browser(new_browser, "New Tab")
self.set_interceptor_for(new_browser)
return new_browser
# ----------------- KONTEKST MENI -----------------
def show_context_menu(self, pos, browser):
cursor_pos = browser.mapToGlobal(pos)
menu = QMenu()
selected_text = browser.page().selectedText()
if selected_text:
menu.addAction("Copy", lambda: QApplication.clipboard().setText(selected_text))
js_code = f"""
(function(){{
var el = document.elementFromPoint({pos.x()}, {pos.y()});
var result = {{
link: null,
image: null
}};
if(el) {{
if(el.closest('a')) result.link = el.closest('a').href;
if(el.tagName.toLowerCase() === 'img') result.image = el.src;
}}
return result;
}})()
"""
def js_callback(res):
def simulate_ctrl_v():
browser.page().runJavaScript("""
var el = document.activeElement;
if(el) el.focus();
""")
event_press = QKeyEvent(QKeyEvent.Type.KeyPress, Qt.Key.Key_V, Qt.KeyboardModifier.ControlModifier)
event_release = QKeyEvent(QKeyEvent.Type.KeyRelease, Qt.Key.Key_V, Qt.KeyboardModifier.ControlModifier)
QApplication.sendEvent(browser.focusProxy(), event_press)
QApplication.sendEvent(browser.focusProxy(), event_release)
menu.addAction("Paste", simulate_ctrl_v)
link_url = res.get("link")
if link_url:
menu.addAction("Open link in new tab", lambda: self.add_new_tab(QUrl(link_url), "New Tab"))
menu.addAction("Open link in new window", lambda: self.handle_new_window(None).setUrl(QUrl(link_url)))
menu.addAction("Copy link address", lambda: QApplication.clipboard().setText(link_url))
menu.addSeparator()
image_url = res.get("image")
if image_url:
menu.addAction("Open image in new tab", lambda: self.add_new_tab(QUrl(image_url), "Image"))
menu.addAction("Open image in new window", lambda: self.handle_new_window(None).setUrl(QUrl(image_url)))
menu.addAction("Copy image URL", lambda: QApplication.clipboard().setText(image_url))
# Save image as...
menu.addAction("Save image as...", lambda: self.save_image_as(image_url))
menu.addSeparator()
menu.addAction("Back", lambda: browser.back())
menu.addAction("Forward", lambda: browser.forward())
menu.addAction("Reload", lambda: browser.reload())
# Save page
menu.addSeparator()
menu.addAction("Save page", lambda: self.save_page(browser))
# View source
menu.addAction("View Page Source", lambda: self.view_source(browser))
# HTML Editor
menu.addAction("Open HTML Editor", lambda: self.open_html_editor(browser))
# Code Extractor
menu.addAction("Code Extractor", lambda: self.export_to_wordpress(browser))
# Translate
menu.addAction("Prevedi stranicu", lambda: self.translate_page(browser))
menu.exec(cursor_pos)
browser.page().runJavaScript(js_code, js_callback)
# ----------------- EXTERNAL FUNKCIONALNOSTI -----------------
def open_html_editor(self, browser):
try:
current_url = browser.url().toString()
import subprocess
subprocess.Popen([sys.executable, "htmleditor.py", current_url])
self.statusBar().showMessage("Started htmleditor.py", 3000)
except Exception as e:
self.statusBar().showMessage(f"Error: {e}", 5000)
def export_to_wordpress(self, browser):
try:
current_url = browser.url().toString()
import subprocess
subprocess.Popen([sys.executable, "wordpress2code.py", current_url])
self.statusBar().showMessage(f"Exporting {current_url} via wordpress2code.py", 3000)
except Exception as e:
self.statusBar().showMessage(f"Error: {e}", 5000)
def translate_page(self, browser):
try:
current_url = browser.url().toString()
if not current_url:
self.statusBar().showMessage("Nema URL adrese za prevođenje", 3000)
return
import urllib.parse
encoded_url = urllib.parse.quote(current_url, safe='')
translate_url = f"https://translate.google.com/translate?sl=auto&tl=sr&u={encoded_url}"
browser.setUrl(QUrl(translate_url))
self.statusBar().showMessage("Stranica se prevodi preko Google Translate...", 3000)
except Exception as e:
self.statusBar().showMessage(f"Greška pri prevođenju: {e}", 5000)
# ----------------- POMOĆNE FUNKCJE -----------------
def save_page(self, browser):
filename, _ = QFileDialog.getSaveFileName(self, "Save Page As", "", "HTML Files (*.html);;All Files (*)")
if filename:
def save_html(html):
try:
with open(filename, 'w', encoding='utf-8') as f:
f.write(html)
self.statusBar().showMessage(f"Stranica sačuvana: {filename}", 3000)
except Exception as e:
self.statusBar().showMessage(f"Greška pri sačuvanju stranice: {e}", 5000)
browser.page().toHtml(save_html)
def save_image_as(self, image_url):
filename, _ = QFileDialog.getSaveFileName(self, "Save Image As", image_url.split("/")[-1],
"Images (*.png *.jpg *.jpeg *.gif *.bmp);;All Files (*)")
if filename:
try:
r = requests.get(image_url, stream=True)
if r.status_code == 200:
with open(filename, 'wb') as f:
for chunk in r.iter_content(1024):
f.write(chunk)
except Exception as e:
self.statusBar().showMessage(f"Error saving image: {e}", 5000)
def view_source(self, browser):
browser.page().toHtml(lambda html: self.show_source_window(html))
def show_source_window(self, html):
w = QMainWindow()
w.setWindowTitle("Page Source")
text = QPlainTextEdit()
text.setPlainText(html)
w.setCentralWidget(text)
w.setGeometry(100, 100, 800, 600)
w.show()
self.source_windows.append(w)
def close_current_tab(self, i):
if self.tabs.count() < 2:
return
self.tabs.removeTab(i)
def current_browser(self):
return self.tabs.currentWidget()
def current_tab_changed(self, i):
qurl = self.current_browser().url()
self.update_urlbar(qurl, self.current_browser())
def navigate_home(self):
self.current_browser().setUrl(QUrl(HOME_URL))
def navigate_to_url(self):
q = QUrl(self.urlbar.text())
if q.scheme() == "":
q.setScheme("https")
self.current_browser().setUrl(q)
def update_urlbar(self, q, browser=None):
if browser != self.current_browser():
return
self.urlbar.setText(q.toString())
self.urlbar.setCursorPosition(0)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Browser()
window.show()
sys.exit(app.exec())
#MIT_License.txt MIT License Copyright (c) [2025] [Aleksandar Maričić] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
