Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to QWebEngine #27

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions qhangups/WebEnginePage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWebEngineWidgets import QWebEnginePage


class WebEnginePage(QWebEnginePage):
def __init__(self, parent):
super(WebEnginePage, self).__init__(parent)

def acceptNavigationRequest(self, url, typ, ismainframe):
if typ == QWebEnginePage.NavigationTypeLinkClicked:
QDesktopServices.openUrl(url)
return False
return super(WebEnginePage, self).acceptNavigationRequest(url, typ, ismainframe)

66 changes: 37 additions & 29 deletions qhangups/conversationwidget.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import datetime, asyncio, logging

from PyQt5 import QtCore, QtGui, QtWidgets, QtWebKitWidgets
from bs4 import BeautifulSoup
from PyQt5 import QtCore, QtGui, QtWidgets

import hangups
from hangups.ui.utils import get_conv_name
Expand All @@ -24,6 +24,7 @@ def __init__(self, tab_parent, client, conv, parent=None):
self.is_loading = False
self.first_loaded = False
self.scroll_prev_height = None
self.html = None

settings = QtCore.QSettings()

Expand All @@ -36,9 +37,8 @@ def __init__(self, tab_parent, client, conv, parent=None):

self.messageTextEdit.textChanged.connect(self.on_text_changed)
self.sendButton.clicked.connect(self.on_send_clicked)
self.messagesWebView.page().mainFrame().contentsSizeChanged.connect(self.on_contents_size_changed)
self.messagesWebView.page().linkClicked.connect(self.on_link_clicked)
self.messagesWebView.page().scrollRequested.connect(self.on_scroll_requested)
self.messagesWebView.page().contentsSizeChanged.connect(self.on_contents_size_changed)
self.messagesWebView.page().scrollPositionChanged.connect(self.on_scroll_position_changed)

self.enter_send_message = settings.value("enter_send_message", False, type=bool)

Expand Down Expand Up @@ -100,13 +100,12 @@ def set_title(self):
def init_messages(self):
"""Initialize QWebView with list of messages"""
self.messagesWebView.setContextMenuPolicy(QtCore.Qt.NoContextMenu)
self.messagesWebView.page().setLinkDelegationPolicy(QtWebKitWidgets.QWebPage.DelegateAllLinks)
#self.messagesWebView.settings().setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True)
self.messagesWebView.setHtml(
# self.messagesWebView.settings().setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True)
self.messagesWebView.page().setHtml(
"""<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta charset="utf-8"></meta>
<title>Messages</title>
<style>
html {{ font-family: sans-serif; font-size: 10pt; color: {0}; }}
Expand All @@ -133,18 +132,30 @@ def add_message(self, timestamp, text, username=None, user_id=None, message_id=N

datestr = "%d.%m. %H:%M" if timestamp.astimezone(tz=None).date() < datetime.date.today() else "%H:%M"
link = "https://plus.google.com/u/0/{}/about".format(user_id) if user_id else ""
message = ("""<div class="message"><b>{}{}:</b><br>\n{}<br>\n</div>\n""").format(
message = ("""<div class="message"><b>{}{}:</b><br/>\n{}<br/>\n</div>\n""").format(
timestamp.astimezone(tz=None).strftime(datestr),
""" | <a href="{}">{}</a>""".format(link, username) if username is not None else "",
text
)
doc = self.messagesWebView.page().mainFrame().documentElement()
div = doc.findFirst("div[id=messages]")

if prepend:
div.prependInside(message)
else:
div.appendInside(message)
self.messagesWebView.page().toHtml(self.on_html_received(prepend, message))

def on_html_received(self, prepend, message_str):
def innerfunc(html):
if "messages" not in html:
return
if self.html is None:
self.html = html
root = BeautifulSoup(self.html, 'html.parser')
message = BeautifulSoup(message_str, 'html.parser')
div = root.find(id='messages')
if div is not None:
if prepend:
div.insert(0, message)
else:
div.append(message)
self.html = str(root)
self.messagesWebView.page().setHtml(self.html)
return innerfunc

def is_current(self):
"""Is this conversation in current tab?"""
Expand Down Expand Up @@ -183,7 +194,7 @@ def load_events(self):
conv_events = []

if conv_events:
self.scroll_prev_height = self.messagesWebView.page().mainFrame().contentsSize().height()
self.scroll_prev_height = self.messagesWebView.page().contentsSize().height()
else:
self.first_loaded = True

Expand All @@ -194,26 +205,27 @@ def load_events(self):

def scroll_messages(self, position=None):
"""Scroll list of messages to given position (or to the bottom if not specified)"""
frame = self.messagesWebView.page().mainFrame()
frame = self.messagesWebView.page()

if position is None:
position = frame.scrollBarMaximum(QtCore.Qt.Vertical)

frame.setScrollPosition(QtCore.QPoint(0, position))

def on_scroll_requested(self, dx, dy, rect_to_scroll):
def on_scroll_position_changed(self, pos):
"""User has scrolled in messagesWebView (callback)"""
frame = self.messagesWebView.page().mainFrame()
if frame.scrollPosition().y() == frame.scrollBarMinimum(QtCore.Qt.Vertical):
if pos.y() == 0:
future = asyncio.async(self.load_events())
future.add_done_callback(lambda future: future.result())
future.add_done_callback(lambda f: f.result())

def on_contents_size_changed(self, size):
"""Size of contents in messagesWebView changed (callback)"""
return
# TODO viewportSize is not implemented in QtWebEngine yet
page = self.messagesWebView.page()
viewport_height = page.viewportSize().height()
contents_height = page.mainFrame().contentsSize().height()
scroll_position = page.mainFrame().scrollPosition().y()
contents_height = page.contentsSize().height()
scroll_position = page.scrollPosition().y()

# Compute max. scroll position manually, because
# scrollBarMaximum(QtCore.Qt.Vertical) doesn't work here
Expand All @@ -232,10 +244,6 @@ def on_contents_size_changed(self, size):
# Use singl-shot timer, because scrolling doesn't work here (maybe some Qt bug?)
QtCore.QTimer.singleShot(0, lambda: self.scroll_messages(position))

def on_link_clicked(self, url):
"""Open links in external web browser (callback)"""
QtGui.QDesktopServices.openUrl(url)

def on_text_changed(self):
"""Message text changed (callback)"""
pass
Expand Down
4 changes: 2 additions & 2 deletions qhangups/ui_qhangupsbrowser.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def setupUi(self, QHangupsBrowser):
QHangupsBrowser.resize(600, 450)
self.verticalLayout = QtWidgets.QVBoxLayout(QHangupsBrowser)
self.verticalLayout.setObjectName("verticalLayout")
self.browserWebView = QtWebKitWidgets.QWebView(QHangupsBrowser)
self.browserWebView = QtWebEngineWidgets.QWebEngineView(QHangupsBrowser)
self.browserWebView.setUrl(QtCore.QUrl("about:blank"))
self.browserWebView.setObjectName("browserWebView")
self.verticalLayout.addWidget(self.browserWebView)
Expand All @@ -26,4 +26,4 @@ def retranslateUi(self, QHangupsBrowser):
_translate = QtCore.QCoreApplication.translate
QHangupsBrowser.setWindowTitle(_translate("QHangupsBrowser", "QHangups - Browser"))

from PyQt5 import QtWebKitWidgets
from PyQt5 import QtWebEngineWidgets
8 changes: 6 additions & 2 deletions qhangups/ui_qhangupsconversationwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

from PyQt5 import QtCore, QtGui, QtWidgets

from qhangups.WebEnginePage import WebEnginePage


class Ui_QHangupsConversationWidget(object):
def setupUi(self, QHangupsConversationWidget):
QHangupsConversationWidget.setObjectName("QHangupsConversationWidget")
Expand All @@ -30,9 +33,10 @@ def setupUi(self, QHangupsConversationWidget):
self.verticalLayout = QtWidgets.QVBoxLayout(self.messagesFrame)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
self.messagesWebView = QtWebKitWidgets.QWebView(self.messagesFrame)
self.messagesWebView = QtWebEngineWidgets.QWebEngineView(self.messagesFrame)
self.messagesWebView.setUrl(QtCore.QUrl("about:blank"))
self.messagesWebView.setObjectName("messagesWebView")
self.messagesWebView.setPage(WebEnginePage(self.messagesWebView))
self.verticalLayout.addWidget(self.messagesWebView)
self.frame = QtWidgets.QFrame(self.splitter)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
Expand Down Expand Up @@ -69,4 +73,4 @@ def retranslateUi(self, QHangupsConversationWidget):
self.sendButton.setText(_translate("QHangupsConversationWidget", "Send"))
self.sendButton.setShortcut(_translate("QHangupsConversationWidget", "Ctrl+Return"))

from PyQt5 import QtWebKitWidgets
from PyQt5 import QtWebEngineWidgets
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
install_requires = [
"hangups>=0.4.1",
"appdirs",
"Quamash"
"Quamash",
"beautifulsoup4"
]

if sys.version_info < (3, 4):
Expand Down