pyqt4 Segmentation fault (core dumped) when enabling plugins - python

I'm working on a python web-browser using PyQt4/QtWebKit and I need to enable plugins so it can render flash content.
When I add this line I get a core dump QWebSettings.globalSettings().setAttribute(QWebSettings.PluginsEnabled, True)
My System
Linux rwilson-Aspire-E5-521 3.16.0-47-generic #63~14.04.1-Ubuntu SMP Fri Aug 14 22:56:59 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
PyQt Version
('Qt version:', '4.8.6')
('SIP version:', '4.15.5')
('PyQt version:', '4.10.4')
Here is all the code
#!/usr/bin/env python
from PyQt4 import QtCore, QtGui, QtNetwork, QtWebKit
from PyQt4.QtWebKit import QWebSettings
QWebSettings.globalSettings().setAttribute(QWebSettings.PluginsEnabled, True)
try:
import jquery_rc3
except ImportError:
import jquery_rc2
class MainWindow(QtGui.QMainWindow):
def __init__(self, url):
super(MainWindow, self).__init__()
self.progress = 0
fd = QtCore.QFile(":/jquery.min.js")
if fd.open(QtCore.QIODevice.ReadOnly | QtCore.QFile.Text):
self.jQuery = QtCore.QTextStream(fd).readAll()
fd.close()
else:
self.jQuery = ''
QtNetwork.QNetworkProxyFactory.setUseSystemConfiguration(True)
self.view = QtWebKit.QWebView(self)
self.view.load(url)
self.view.loadFinished.connect(self.adjustLocation)
self.view.titleChanged.connect(self.adjustTitle)
self.view.loadProgress.connect(self.setProgress)
self.view.loadFinished.connect(self.finishLoading)
self.locationEdit = QtGui.QLineEdit(self)
self.locationEdit.setSizePolicy(QtGui.QSizePolicy.Expanding,
self.locationEdit.sizePolicy().verticalPolicy())
self.locationEdit.returnPressed.connect(self.changeLocation)
toolBar = self.addToolBar("Navigation")
toolBar.addAction(self.view.pageAction(QtWebKit.QWebPage.Back))
toolBar.addAction(self.view.pageAction(QtWebKit.QWebPage.Forward))
toolBar.addAction(self.view.pageAction(QtWebKit.QWebPage.Reload))
toolBar.addAction(self.view.pageAction(QtWebKit.QWebPage.Stop))
toolBar.addWidget(self.locationEdit)
viewMenu = self.menuBar().addMenu("&View")
viewSourceAction = QtGui.QAction("Page Source", self)
viewSourceAction.triggered.connect(self.viewSource)
viewMenu.addAction(viewSourceAction)
effectMenu = self.menuBar().addMenu("&Effect")
effectMenu.addAction("Highlight all links", self.highlightAllLinks)
self.rotateAction = QtGui.QAction(
self.style().standardIcon(
QtGui.QStyle.SP_FileDialogDetailedView),
"Turn images upside down", self, checkable=True,
toggled=self.rotateImages)
effectMenu.addAction(self.rotateAction)
toolsMenu = self.menuBar().addMenu("&Tools")
toolsMenu.addAction("Remove GIF images", self.removeGifImages)
toolsMenu.addAction("Remove all inline frames",
self.removeInlineFrames)
toolsMenu.addAction("Remove all object elements",
self.removeObjectElements)
toolsMenu.addAction("Remove all embedded elements",
self.removeEmbeddedElements)
self.setCentralWidget(self.view)
self.setUnifiedTitleAndToolBarOnMac(True)
def viewSource(self):
accessManager = self.view.page().networkAccessManager()
request = QtNetwork.QNetworkRequest(self.view.url())
reply = accessManager.get(request)
reply.finished.connect(self.slotSourceDownloaded)
def slotSourceDownloaded(self):
reply = self.sender()
self.textEdit = QtGui.QTextEdit(None)
self.textEdit.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.textEdit.show()
self.textEdit.setPlainText(QtCore.QTextStream(reply).readAll())
self.textEdit.resize(600, 400)
reply.deleteLater()
def adjustLocation(self):
self.locationEdit.setText(self.view.url().toString())
def changeLocation(self):
url = QtCore.QUrl.fromUserInput(self.locationEdit.text())
self.view.load(url)
self.view.setFocus()
def adjustTitle(self):
if 0 < self.progress < 100:
self.setWindowTitle("%s (%s%%)" % (self.view.title(), self.progress))
else:
self.setWindowTitle(self.view.title())
def setProgress(self, p):
self.progress = p
self.adjustTitle()
def finishLoading(self):
self.progress = 100
self.adjustTitle()
self.view.page().mainFrame().evaluateJavaScript(self.jQuery)
self.rotateImages(self.rotateAction.isChecked())
def highlightAllLinks(self):
code = """$('a').each(
function () {
$(this).css('background-color', 'yellow')
}
)"""
self.view.page().mainFrame().evaluateJavaScript(code)
def rotateImages(self, invert):
if invert:
code = """
$('img').each(
function () {
$(this).css('-webkit-transition', '-webkit-transform 2s');
$(this).css('-webkit-transform', 'rotate(180deg)')
}
)"""
else:
code = """
$('img').each(
function () {
$(this).css('-webkit-transition', '-webkit-transform 2s');
$(this).css('-webkit-transform', 'rotate(0deg)')
}
)"""
self.view.page().mainFrame().evaluateJavaScript(code)
def removeGifImages(self):
code = "$('[src*=gif]').remove()"
self.view.page().mainFrame().evaluateJavaScript(code)
def removeInlineFrames(self):
code = "$('iframe').remove()"
self.view.page().mainFrame().evaluateJavaScript(code)
def removeObjectElements(self):
code = "$('object').remove()"
self.view.page().mainFrame().evaluateJavaScript(code)
def removeEmbeddedElements(self):
code = "$('embed').remove()"
self.view.page().mainFrame().evaluateJavaScript(code)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
if len(sys.argv) > 1:
url = QtCore.QUrl(sys.argv[1])
else:
url = QtCore.QUrl('http://www.google.com/ncr')
browser = MainWindow(url)
browser.show()
sys.exit(app.exec_())

You need to have an instance of QApplication before using QWebSettings.
Try moving the offending line to MainWindow.__init__.

Related

Combining pyOSC with pyQT5 / Threading?

What I try to do:
I want to write a Python program running on an RPI which communicates to a software called QLab4 via OSC. It should display the received workspace data (JSON format) in a table, while the user is able to select a cue from the table and fire it using a key press.
So far I got two scripts (just proof of concept stage), but I struggle to combine them into one program. Any hints to how to do it would be greatly appreciated.
Script 1:
Starts OSC client & server and sends and receives OSC to/from QLAB.
At the moment it just prints out the JSON (workspace) and the selected cue.
import argparse
import time
import threading
import json
from pythonosc import udp_client
from pythonosc import dispatcher
from pythonosc import osc_server
workspace = {}
def print_json(unused_addr, args):
global workspace
workspace = json.loads(args)
print(workspace)
def print_name(unused_addr, args):
decoded_json = json.loads(args)
print(args)
print(type(decoded_json['data']))
data = decoded_json['data']
print("Cue Name: " + data['displayName'])
print("Cue Number: " + data['number'])
def print_payload(unused_addr, args):
print ("Selected Cue: " + args)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--serverip", default="127.0.0.1", help="The ip to listen on")
parser.add_argument("--serverport", type=int, default=53001, help="The port the OSC Server is listening on")
parser.add_argument("--clientip", default="127.0.0.1", help="The ip of the OSC server")
parser.add_argument("--clientport", type=int, default=53000, help="The port the OSC Client is listening on")
args = parser.parse_args()
# listen to addresses and print changes in values
dispatcher = dispatcher.Dispatcher()
dispatcher.map("/reply/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/cueLists",print_json)
dispatcher.map("/update/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/cueList/*/playbackPosition",print_payload)
dispatcher.map("/reply/cue_id/*/valuesForKeys", print_json)
dispatcher.map("/reply/cue_id/*/valuesForKeys", print_name)
def start_server(ip, port):
print("Starting Server")
server = osc_server.ThreadingOSCUDPServer(
(ip, port), dispatcher)
print("Serving on {}".format(server.server_address))
thread = threading.Thread(target=server.serve_forever)
thread.start()
def start_client(ip, port):
print("Starting Client")
client = udp_client.SimpleUDPClient(ip, port)
# print("Sending on {}".format(client.))
thread = threading.Thread(target=get_workspace(client))
thread2 = threading.Thread(target=print_active(client))
thread.start()
thread2.start()
# send random values between 0-1 to the three addresses
def get_workspace(client):
client.send_message("/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/cueLists", 1)
client.send_message("/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/updates", 1)
def print_active(client):
while True:
client.send_message("/cue/active/valuesForKeys", "[\"displayName\",\"number\", \"type\", \"isBroken\", "
"\"isLoaded\", \"isPaused\", \"isRunning\", \"preWait\", \"duration\", \"postWait\"]")
time.sleep(1)
start_server(args.serverip, 53001)
start_client(args.clientip, 53000)
Script 2:
I copied the received JSON string of the workspace as variable. Then it builds a table using pyQt5. After hitting "R" it inserts a row for each received cue.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import json
# var
cue_count = 1
cue_id = ""
# data
data = '{"status":"ok","data":[{"number":"","uniqueID":"0CBC2F3A-B4CB-46A0-AE74-0810598256AD","cues":' \
'[{"number":"1","uniqueID":"C358F03C-128B-4BBA-B8D8-3E1C3D217775","flagged":false,' \
'"listName":"Cue 1","type":"Memo","colorName":"none","name":"Cue 1","armed":true},' \
'{"number":"2","uniqueID":"F2FDC19F-8E4F-43E6-B49F-BA421C886E63","flagged":false,' \
'"listName":"Cue 2","type":"Memo","colorName":"none","name":"Cue 2","armed":true},' \
'{"number":"3","uniqueID":"244B2654-51B3-4423-AF1A-6894F301CA6B","flagged":false,' \
'"listName":"Cue 3","type":"Memo","colorName":"none","name":"Cue 3","armed":true},' \
'{"number":"4","uniqueID":"42B37827-A5E8-444D-82D1-7706A8E197A6","flagged":false,' \
'"listName":"Cue 4","type":"Memo","colorName":"none","name":"Cue 4","armed":true}],' \
'"flagged":false,"listName":"Main Cue List","type":"Cue List","colorName":"none",' \
'"name":"Main Cue List","armed":true}],"workspace_id":"C936DAAF-D5C7-4D1D-8382-54CA426A1BDC",' \
'"address":"\/workspace\/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC\/cueLists"}'
class TableFromList(QTableWidget):
def __init__(self, data, *args):
# Call parent constructor
QTableWidget.__init__(self, *args)
# Set the necessary configurations fot the table
self.data = data
self.resizeColumnsToContents()
self.resizeRowsToContents()
self.setColumnWidth(0, 100)
self.setColumnWidth(0, 40)
self.setColumnWidth(1, 200)
self.setColumnWidth(2, 80)
self.setColumnWidth(3, 80)
self.setMinimumWidth(400)
self.setWindowTitle("WORKSPACE")
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.setStyleSheet("color:white; background-color:black; font-weight: bold; selection-background-color: orange")
self.setFont(QFont('Arial', 14))
# Declare the variable to set the header content
headers = ["No.", "NAME", "Armed"]
# Set the header label of the table
self.setHorizontalHeaderLabels(headers)
# Read the particular cell value
self.clicked.connect(self.on_click)
self.currentItemChanged.connect(self.select_cue)
# Display the window in the center of the screen
win = self.frameGeometry()
pos = QDesktopWidget().availableGeometry().center()
win.moveCenter(pos)
self.move(win.topLeft())
self.show()
def on_click(self):
for ItemVal in self.selectedItems():
# Read the header value based on the selected cell
subject = self.horizontalHeaderItem(ItemVal.column()).text()
# Print the detail information of the mark
print("\n", self.ID_list[ItemVal.row()], " got ", ItemVal.text(), " in ", subject)
def select_cue(self):
global cue_id
global data
data_dict = json.loads(data)
data_parsed = data_dict["data"][0]["cues"]
value = self.currentRow()
cue = self.item(value, 0)
cue_number = cue.text()
cue_number = int(cue_number) - 1
cue_id = data_parsed[cue_number]["uniqueID"]
print("ID: " + cue_id)
print("Cue Number is: " + cue.text())
def keyPressEvent(self, keyEvent):
super(TableFromList, self).keyPressEvent(keyEvent)
data = self.data
def build_cuelist(data):
data_dict = json.loads(data)
cue_count = len(data_dict["data"][0]["cues"])
data = data_dict["data"][0]["cues"]
self.setRowCount(0)
for i in range(cue_count):
rowPosition = table.rowCount()
table.insertRow(rowPosition) # insert new row
self.setItem(rowPosition, 0, QTableWidgetItem(str(data[i]["number"])))
self.setItem(rowPosition, 1, QTableWidgetItem(str(data[i]["name"])))
self.setItem(rowPosition, 2, QTableWidgetItem(str(data[i]["armed"])))
if keyEvent.key() == Qt.Key_Return:
print('*** Return pressed')
print("OSC: /cue/" + cue_id + "/go")
elif keyEvent.key() == Qt.Key_Enter:
print('*** Enter pressed')
elif keyEvent.key() == Qt.Key_1:
print('*** 1 pressed')
elif keyEvent.key() == Qt.Key_R:
build_cuelist(self.data)
# Create app object and execute the app
app = QApplication(sys.argv)
table = TableFromList(data, 0, 3)
table.show()
app.exec()
The code provided by the OP has various errors such as threads not being executed in a second thread, the code works because threads are not needed.
On the other hand, signals must be used to send the information from the callbacks to the widgets.
import json
import sys
import threading
from dataclasses import dataclass
from functools import cached_property
from PyQt5 import QtCore, QtGui, QtWidgets
from pythonosc import dispatcher
from pythonosc import osc_server
from pythonosc import udp_client
#dataclass
class Server(QtCore.QObject):
ip: str = "127.0.0.1"
port: int = 53001
datachanged = QtCore.pyqtSignal(dict)
def __post_init__(self):
super().__init__()
#cached_property
def osc_server(self):
dp = dispatcher.Dispatcher()
dp.map(
"/reply/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/cueLists",
self.handle_reply_workspace,
)
dp.map(
"/update/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/cueList/*/playbackPosition",
self.handle_update_workspace,
)
dp.map(
"/cue/active/valuesForKeys",
self.handle_cue_id,
)
return osc_server.ThreadingOSCUDPServer((self.ip, self.port), dp)
def start(self):
threading.Thread(target=self.osc_server.serve_forever, daemon=True).start()
def handle_reply_workspace(self, unused_addr, data):
try:
d = json.loads(data)
except json.JSONDecodeError as e:
print(str(e))
else:
self.datachanged.emit(d)
def handle_update_workspace(self, unused_addr, data):
print(data)
def handle_cue_id(self, unused_addr, data):
print(data)
#dataclass
class Client(QtCore.QObject):
ip: str = "127.0.0.1"
port: int = 53000
datachanged = QtCore.pyqtSignal(dict)
def __post_init__(self):
super().__init__()
def start_timer(self, dt=1000):
self.timer.start(dt)
def request_workspace(self):
self.osc_client.send_message(
"/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/cueLists", 1
)
self.osc_client.send_message(
"/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/updates", 1
)
#cached_property
def osc_client(self):
return udp_client.SimpleUDPClient(self.ip, self.port)
#cached_property
def timer(self):
timer = QtCore.QTimer()
timer.timeout.connect(self.handle_timeout)
return timer
#QtCore.pyqtSlot()
def handle_timeout(self):
keys = [
"displayName",
"number",
"type",
"isBroken",
"isLoaded",
"isPaused",
"isRunning",
"preWait",
"duration",
"postWait",
]
self.osc_client.send_message(
"/cue/active/valuesForKeys",
json.dumps(keys),
)
class TableWidget(QtWidgets.QTableWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setColumnCount(3)
self.setHorizontalHeaderLabels(["No.", "NAME", "Armed"])
self.setWindowTitle("WORKSPACE")
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.setStyleSheet(
"""
color:white;
background-color:black;
font-weight: bold;
selection-background-color: orange
"""
)
self.setFont(QtGui.QFont("Arial", 14))
self.setColumnWidth(0, 40)
self.setColumnWidth(1, 200)
self.setColumnWidth(2, 80)
self.setMinimumWidth(400)
#QtCore.pyqtSlot(dict)
def update_data(self, data):
self.setRowCount(0)
keys = ("number", "name", "armed")
cues = data["data"][0]["cues"]
for i, cue in enumerate(cues):
self.insertRow(self.rowCount())
for j, key in enumerate(keys):
value = cue[key]
item = QtWidgets.QTableWidgetItem()
item.setData(QtCore.Qt.DisplayRole, value)
self.setItem(i, j, item)
def main():
app = QtWidgets.QApplication(sys.argv)
server = Server()
server.start()
client = Client()
client.request_workspace()
client.start_timer()
table_widget = TableWidget()
table_widget.resize(640, 480)
table_widget.show()
server.datachanged.connect(table_widget.update_data)
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Add a large shapefile to map in python using folium

I am displaying a folium map in my application using python, PyQt5 and Qt designer. Since there is no map widget in Qt designer, I add a general widget and then promote it to my custom map widget. It all works fine. Here is the python code for my promoted widget:
import io
import folium
from PyQt5 import QtWebEngineWidgets
from PyQt5.QtWidgets import *
class LeafWidget (QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
m = folium.Map(
location=[40, -120] , zoom_start=10
)
self.view = QtWebEngineWidgets.QWebEngineView()
data = io.BytesIO()
m.save(data, close_file=False)
self.view.setHtml(data.getvalue().decode())
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.view)
self.show()
This works fine and I can see the map in my application.
I am also trying to display a GIS shapefile on top of this map. I have done some research and it seems like I cannot add GIS shapefile (.shp) directly to a folium map. So, I try to convert it to json first and then add the json on top of the map. I modified my code as below to add the .shp file to map:
import io
import folium
import os.path
from PyQt5 import QtWebEngineWidgets
from PyQt5.QtWidgets import *
import geopandas as gpd
class LeafWidget (QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
m = folium.Map(
location=[40, -120] , zoom_start=10
)
self.view = QtWebEngineWidgets.QWebEngineView()
# converting shp to geojson
shp_file = gpd.read_file('input/2015_loaded_NoCC.shp')
shp_file.to_file('myshpfile.json', driver='GeoJSON')
shp = os.path.join('', 'myshpfile.json')
data = io.BytesIO()
folium.GeoJson(shp).add_to(m)
m.save(data, close_file=False)
self.view.setHtml(data.getvalue().decode())
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.view)
self.show()
but now my map doesn't show up at all. It's just an empty space with no errors in the console or error log. If I save the map as an HTML file using "m.save('map.html')" though, it does save the file and when I open it, it displays the json file on the map, but for some reason, the way I am doing it to show the map in my application is not working after adding the shp-->json file. What am I doing wrong?
As already pointed out in these questions(1 and 2) and in the official docs:
void QWebEnginePage::setHtml(const QString &html, const QUrl &baseUrl
= QUrl()) Sets the content of this page to html. baseUrl is optional and used to resolve relative URLs in the document, such as referenced
images or stylesheets.
The html is loaded immediately; external objects are loaded
asynchronously.
If a script in the html runs longer than the default script timeout
(currently 10 seconds), for example due to being blocked by a modal
JavaScript alert dialog, this method will return as soon as possible
after the timeout and any subsequent html will be loaded
asynchronously.
When using this method, the web engine assumes that external
resources, such as JavaScript programs or style sheets, are encoded in
UTF-8 unless otherwise specified. For example, the encoding of an
external script can be specified through the charset attribute of the
HTML script tag. It is also possible for the encoding to be specified
by the web server.
This is a convenience function equivalent to setContent(html,
"text/html", baseUrl).
Note: This method will not affect session or global history for the
page.
Warning: This function works only for HTML, for other mime types (such
as XHTML and SVG) setContent() should be used instead.
Warning: The content will be percent encoded before being sent to the
renderer via IPC. This may increase its size. The maximum size of the
percent encoded content is 2 megabytes minus 30 bytes.
(emphasis mine)
setHtml() does not support content greater than 2MB, so in your particular case there are 2 solutions:
Save the folium map in an html file:
import io
import os
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class LeafWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self.view = QtWebEngineWidgets.QWebEngineView()
shp_filename = os.path.join(CURRENT_DIR, "input", "2015_loaded_NoCC.shp")
shp_file = gpd.read_file(shp_filename)
shp_file_json_str = shp_file.to_json()
m = folium.Map(location=[40, -120], zoom_start=10)
folium.GeoJson(shp_file_json_str).add_to(m)
tmp_file = QtCore.QTemporaryFile("XXXXXX.html", self)
if tmp_file.open():
m.save(tmp_file.fileName())
url = QtCore.QUrl.fromLocalFile(tmp_file.fileName())
self.view.load(url)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.view)
def main():
app = QtWidgets.QApplication([])
w = LeafWidget()
w.show()
app.exec_()
if __name__ == "__main__":
main()
Use a QWebEngineUrlSchemeHandler to return the html:
qfolium.py
import json
import io
from PyQt5 import QtCore, QtWebEngineCore, QtWebEngineWidgets
class FoliumSchemeHandler(QtWebEngineCore.QWebEngineUrlSchemeHandler):
def __init__(self, app):
super().__init__(app)
self.m_app = app
def requestStarted(self, request):
url = request.requestUrl()
name = url.host()
m = self.m_app.process(name, url.query())
if m is None:
request.fail(QtWebEngineCore.QWebEngineUrlRequestJob.UrlNotFound)
return
data = io.BytesIO()
m.save(data, close_file=False)
raw_html = data.getvalue()
buf = QtCore.QBuffer(parent=self)
request.destroyed.connect(buf.deleteLater)
buf.open(QtCore.QIODevice.WriteOnly)
buf.write(raw_html)
buf.seek(0)
buf.close()
request.reply(b"text/html", buf)
class FoliumApplication(QtCore.QObject):
scheme = b"folium"
def __init__(self, parent=None):
super().__init__(parent)
scheme = QtWebEngineCore.QWebEngineUrlScheme(self.scheme)
QtWebEngineCore.QWebEngineUrlScheme.registerScheme(scheme)
self.m_functions = dict()
def init_handler(self, profile=None):
if profile is None:
profile = QtWebEngineWidgets.QWebEngineProfile.defaultProfile()
handler = profile.urlSchemeHandler(self.scheme)
if handler is not None:
profile.removeUrlSchemeHandler(handler)
self.m_handler = FoliumSchemeHandler(self)
profile.installUrlSchemeHandler(self.scheme, self.m_handler)
def register(self, name):
def decorator(f):
self.m_functions[name] = f
return f
return decorator
def process(self, name, query):
f = self.m_functions.get(name)
if f is None:
print("not found")
return
items = QtCore.QUrlQuery(query).queryItems()
params_json = dict(items).get("json", None)
if params_json is not None:
return f(**json.loads(params_json))
return f()
def create_url(self, name, params=None):
url = QtCore.QUrl()
url.setScheme(self.scheme.decode())
url.setHost(name)
if params is not None:
params_json = json.dumps(params)
query = QtCore.QUrlQuery()
query.addQueryItem("json", params_json)
url.setQuery(query)
return url
main.py
import io
import os
import folium
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
import geopandas as gpd
from qfolium import FoliumApplication
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
folium_app = FoliumApplication()
#folium_app.register("load_shapefile")
def load_shapefile(latitude, longitude, zoom_start, shp_filename):
shp_file = gpd.read_file(shp_filename)
shp_file_json_str = shp_file.to_json()
m = folium.Map(
location=[latitude, longitude], zoom_start=zoom_start
)
folium.GeoJson(shp_file_json_str).add_to(m)
print(m)
return m
class LeafWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self.view = QtWebEngineWidgets.QWebEngineView()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.view)
self.resize(640, 480)
shp_filename = os.path.join(CURRENT_DIR, "input", "2015_loaded_NoCC.shp")
params = {
"shp_filename": shp_filename,
"latitude": 40,
"longitude": -120,
"zoom_start": 5,
}
url = folium_app.create_url("load_shapefile", params=params)
self.view.load(url)
def main():
app = QtWidgets.QApplication([])
folium_app.init_handler()
w = LeafWidget()
w.show()
app.exec_()
if __name__ == "__main__":
main()

Why function defined in class is not visible in other fragments of code?

I am playing with python, being completely new at that. I wrote my first "serious" piece of code using tkinter and beautifulsoup and stuff and it worked. Now, trying to expand my knowledge I am re-writing it using pyqt5 and trying to use classes instead of "spaghetti" code.
My program in general works, it reads website, parses the html code with BeautifulSoup, gets required lines, etc. I used some "calculator" tutorial to base it on and with many trials and errors I made it work. Code below:
from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout, QPushButton, QFileDialog, QMainWindow
from PyQt5.QtCore import Qt, QDate, QTimer, QEventLoop
from PyQt5.QtGui import QIcon, QPixmap
import sys
import os
import requests
from bs4 import BeautifulSoup
from operator import itemgetter
ERROR_MSG = 'ERROR'
class BinColUI(QMainWindow):
def createLabelTop(self):
self.label_top = QLabel('PLEASE WAIT')
self.label_top.setAlignment(Qt.AlignCenter)
self.label_top.setStyleSheet("font: 14pt Bahnschrift; color: yellow")
self.generalLayout.addWidget(self.label_top, alignment=Qt.AlignCenter)
def createLabelBot(self):
self.label_bot = QLabel('PLEASE WAIT')
self.label_bot.setAlignment(Qt.AlignCenter)
self.label_bot.setStyleSheet("font: 14pt Bahnschrift; color: yellow")
self.generalLayout.addWidget(self.label_bot, alignment=Qt.AlignCenter)
def setLabels(self, texttop, textbot):
self.label_top.setText(texttop)
self.label_bot.setText(textbot)
def createLabelImg(self):
label_img = QLabel()
label_img.setFixedSize(self.window().width(), 300)
label_img.setAlignment(Qt.AlignCenter)
image = 'img\pleasewait'
pixmap = QPixmap(resource_path(image+'.png'))
pixmap = pixmap.scaledToHeight(label_img.height(), Qt.SmoothTransformation)
label_img.setPixmap(pixmap)
self.generalLayout.addWidget(label_img, alignment=Qt.AlignCenter)
def setLabelImg(self, bin_color):
image = 'img\'+bin_color'
pixmap = QPixmap(resource_path(image + '.png'))
pixmap = pixmap.scaledToHeight(self.label_img.height(), Qt.SmoothTransformation)
self.label_img.setPixmap(pixmap)
def __init__(self):
super().__init__()
self.setWindowTitle('Bin Collection')
self.setFixedSize(500, 500)
self.setStyleSheet('background-color: #7C7D7B')
self.generalLayout = QVBoxLayout()
self._centralWidget = QWidget(self)
self.setCentralWidget(self._centralWidget)
self._centralWidget.setLayout(self.generalLayout)
self.createLabelTop()
self.createLabelImg()
self.createLabelBot()
class BinColCtrl:
def __init__(self, model, view):
self._evaluate = model
self._view = view
self.calculateResult()
def calculateResult(self):
line_top = parseGoodLines(0)
line_bottom = parseGoodLines(1)
self._view.setLabels(line_top, line_bottom)
self._view.
'''
Why the function setLabelImg from class BinColUi is not visible here?
I can call setLabel (as shown above) but no setLabelImg.
'''
def parseGoodLines(linia_number):
global bin_color
try:
if linia_number==0:
start_text='Your next collection is '
else:
start_text='After that: '
kosz_name = good_lines[linia_number][0]
kosz_date = good_lines[linia_number][1]
kosz_date_str = QDate.toString(kosz_date, 'dd MMMM yyyy')
ile_dni=QDate.currentDate().daysTo(kosz_date)
result = '%s%s\nYou need to put it outside before %s\nIt\'s in %s days' \
%(start_text, str.upper(kosz_name), kosz_date_str, str(ile_dni))
except Exception:
result = ERROR_MSG
return result
def resource_path(relative_path):
base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
return os.path.join(base_path, relative_path)
class MakeSoup:
def getDataFromWebsite(self):
URL = 'http://mydurham.durham.gov.uk/article/12690?uprn=100110375827'
page = requests.get(URL)
soup = BeautifulSoup(page.content, 'html.parser')
results = soup.find(id='page_PageContentHolder_template_pnlArticleBody')
return results
def mixSoup(self, dane):
linie_ze_strony = dane.find_all('p')
global good_lines
good_lines=[]
for kosz in linie_ze_strony:
linia_bez_p = str(kosz).replace('<p>', "")
linia_bez_p = str(linia_bez_p).replace('</p>', "")
kosz = linia_bez_p
if 'Your next ' in str(kosz):
if 'rubbish' in str(kosz):
rubbish_len = len(str(kosz)) - 1
date_rubbish = str(kosz)[33:rubbish_len]
if 'recycling' in str(kosz):
recycle_len = len(str(kosz)) - 1
date_recycle = str(kosz)[35:recycle_len]
qdate_rubbish = QDate.fromString(date_rubbish, 'dd MMMM yyyy')
qdate_recycle = QDate.fromString(date_recycle, 'dd MMMM yyyy')
good_lines.append(['Rubbish', qdate_rubbish])
good_lines.append(['Recycling', qdate_recycle])
good_lines.sort(key=itemgetter(1))
return good_lines
def __init__(self):
self.mixSoup(self.getDataFromWebsite())
def main():
bincol = QApplication(sys.argv)
view = BinColUI()
view.show()
MakeSoup()
model = parseGoodLines
BinColCtrl(model=model, view=view)
sys.exit(bincol.exec_())
if __name__ == '__main__':
main()
In class BinColUi I have some functions which I'm using later on to build and change some visual elements. In class BinColCtrl I change text on the labels using function setLabels form BinColUi class and it works alright. But when I try to call function setLabelImg from the same class it's not visible (see attaached pic) and I can't figure why. In fact only function setLabels is available.
Your IDE does not know the type of self._view. It only knows that self._view has a setLabels attribute because you just used that.
Annotate the variable with the correct type and your IDE can discover the method.
class BinColCtrl:
# ``: BinColUI`` tells the IDE the type of ``view`` and ``self._view``
def __init__(self, model, view: BinColUI):
self._evaluate = model
self._view = view
self.calculateResult()
It seems that your IDE doesn't know type of self._view object. It sees setLabels because it was used in the line above.
Try adding type annotation like following:
class BinColCtrl:
def __init__(self, model, view):
self._evaluate = model
self._view: BinColUi = view # <-- this type annotation might help
self.calculateResult()
You can find more about type annotations here

Output of youtube-dl using QProcess

I am so new to python and am in process of learning this powerful language. I managed to write the following script. It does get a partial output ( only two lines of it ) the I don't know what is going wrong! Please help me.
#!/usr/bin/env python
#-*- coding: utf-8 -*-
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import (QProcess,QRect,SIGNAL,SLOT,QString,QStringList,qDebug)
from PyQt4.QtGui import (QMainWindow,QWidget,QPushButton,QTextEdit,QApplication)
class YTDL (QtGui.QMainWindow):
def __init__ (self,parent=None):
super(YTDL,self).__init__(parent)
self.resize(400,300)
self.setWindowTitle("Youtube-dl output using QProcess")
self.__init_Components()
self.__ui_event_handler()
def __init_Components(self):
self.proc = QProcess()
self.cw = QWidget(self)
self.btn = QPushButton(self.cw)
self.btn.setText("Run")
self.btn.setGeometry(QRect(270,10,110,27))
self.te = QTextEdit(self.cw)
self.te.setReadOnly(True)
self.te.setOverwriteMode(False)
self.te.setGeometry(QRect(10,40,380,170))
self.setCentralWidget(self.cw)
def __ui_event_handler(self):
self.connect(self.btn, SIGNAL('clicked()'),self.Button_Clicked)
def Button_Clicked(self):
args = '-ct -f 18 --extract-audio --audio-quality 320k --audio-format mp3 -k http://www.youtube.com/watch?v=OiPO_TAAZPc'
cmd = 'youtube-dl'
self.proc.setWorkingDirectory("~/Videos/Folder1")
self.connect(self.proc, SIGNAL('readyRead()'),self._read)
self.proc.setOpenMode(self.proc.ReadWrite)
self.proc.start(cmd ,args)
if not self.proc.waitForStarted():
exit(1)
def _read(self):
s = self.proc.readAllStandardOutput()
qDebug (s)
print (s)
self.te.append(QString(s))
def main():
import sys
app = QApplication(sys.argv)
ytdl = YTDL()
ytdl.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I think I figured it out myself. Here is an all improved version below. If there is something I need to improve upon, I will welcome any recommendations.
class YTDL (QtGui.QMainWindow):
def init (self,parent=None):
super(YTDL,self).init(parent)
self.resize(400,350)
self.setWindowTitle("Youtube-dl output using QProcess")
self.__init_Components()
self.__ui_event_handler()
def __init_Components(self):
self.proc = QProcess()
self.cw = QWidget(self)
self.btn = QPushButton(self.cw)
self.btn.setText("Run")
self.btn.setGeometry(QRect(270,10,110,27))
self.te = QTextEdit(self.cw)
self.te.setReadOnly(True)
self.te.setOverwriteMode(False)
self.te.setGeometry(QRect(10,40,380,170))
self.progbar = QProgressBar(self.cw)
self.progbar.setGeometry(QRect(10,220,380,18))
self.progbar.setRange(0,100)
self.progbar.setValue(0)
self.progbar.show()
self.setCentralWidget(self.cw)
def __ui_event_handler(self):
self.connect(self.btn, SIGNAL('clicked()'),self.Button_Clicked)
self.connect(self.proc, SIGNAL('readyReadStandardOutput()'),self._read)
self.connect(self.proc, SIGNAL('readyReadStandardError()'),self._readError)
def Button_Clicked(self):
args = "-ct -f 18 --extract-audio --audio-quality 320k --audio-format mp3 -k http://www.youtube.com/watch?v=SjUrib_Gh0Y"
cmd = "youtube-dl"
cmd = cmd + " " + args
print (cmd)
self.proc.setWorkingDirectory("~/Videos/Folder1")
self.proc.setOpenMode(self.proc.ReadWrite)
self.proc.start(cmd)
self.proc.waitForStarted()
def _read(self):
s = str(self.proc.readAllStandardOutput())
download_progress_exp = re.compile(r'.\d+\.\d+\%', re.MULTILINE)
progbarresult = download_progress_exp.findall(s)
i = 0
if progbarresult != []:
for i in range(0,len(progbarresult)):
self.progbar.setValue(float(progbarresult[i].strip("%")))
self.te.append(QString(s))
def _readError(self):
self.te.append(str(self.proc.readAllStandardError()))

Threading in pyqt4

In my GUI I have to download lots of stuff in between. I use urllib to do that. the problem of course then becomes that the GUI freezes up until everything gets downloaded.
My code is something like following
QtCore.QObject.connect( self.UI.commandLinkButton_2 , QtCore.SIGNAL("clicked()") , self.addStoryToHistory )
wherein the above function has the downloading code.
There is nothing like sending of shared data among this and the process just involves downloading of the data to a location.
What is the simplest way to not freeze up my GUI ? Should i use multiprocessing or QThreads?
Can anybody point me to some links.... I do not wish it to be very complex so if there is any easier way do it please point it out....
Thanks a lot...
Here's an example I've just stripped from a project I was working on a couple of months back using the http example from PyQt as a base. It'll download SIP from the Riverbank website.
It's using QHttp from QtNetwork instead of urllib and the progress bar is connected to its dataReadProgress signal. This should allow you to reliably download a file as well as having a responsive GUI.
from PyQt4.QtCore import QUrl, QFileInfo, QFile, QIODevice
from PyQt4.QtGui import QApplication, QDialog, QProgressBar, QLabel, QPushButton, QDialogButtonBox, \
QVBoxLayout, QMessageBox
from PyQt4.QtNetwork import QHttp
url_to_download = 'http://www.riverbankcomputing.co.uk/static/Downloads/sip4/sip-4.12.3.zip'
class Downloader(QDialog):
def __init__(self, parent=None):
super(Downloader, self).__init__(parent)
self.httpGetId = 0
self.httpRequestAborted = False
self.statusLabel = QLabel('Downloading %s' % url_to_download)
self.closeButton = QPushButton("Close")
self.closeButton.setAutoDefault(False)
self.progressBar = QProgressBar()
buttonBox = QDialogButtonBox()
buttonBox.addButton(self.closeButton, QDialogButtonBox.RejectRole)
self.http = QHttp(self)
self.http.requestFinished.connect(self.httpRequestFinished)
self.http.dataReadProgress.connect(self.updateDataReadProgress)
self.http.responseHeaderReceived.connect(self.readResponseHeader)
self.closeButton.clicked.connect(self.cancelDownload)
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.statusLabel)
mainLayout.addWidget(self.progressBar)
mainLayout.addWidget(buttonBox)
self.setLayout(mainLayout)
self.setWindowTitle('Download Example')
self.downloadFile()
def downloadFile(self):
url = QUrl(url_to_download)
fileInfo = QFileInfo(url.path())
fileName = fileInfo.fileName()
if QFile.exists(fileName):
QFile.remove(fileName)
self.outFile = QFile(fileName)
if not self.outFile.open(QIODevice.WriteOnly):
QMessageBox.information(self, 'Error',
'Unable to save the file %s: %s.' % (fileName, self.outFile.errorString()))
self.outFile = None
return
mode = QHttp.ConnectionModeHttp
port = url.port()
if port == -1:
port = 0
self.http.setHost(url.host(), mode, port)
self.httpRequestAborted = False
path = QUrl.toPercentEncoding(url.path(), "!$&'()*+,;=:#/")
if path:
path = str(path)
else:
path = '/'
# Download the file.
self.httpGetId = self.http.get(path, self.outFile)
def cancelDownload(self):
self.statusLabel.setText("Download canceled.")
self.httpRequestAborted = True
self.http.abort()
self.close()
def httpRequestFinished(self, requestId, error):
if requestId != self.httpGetId:
return
if self.httpRequestAborted:
if self.outFile is not None:
self.outFile.close()
self.outFile.remove()
self.outFile = None
return
self.outFile.close()
if error:
self.outFile.remove()
QMessageBox.information(self, 'Error',
'Download failed: %s.' % self.http.errorString())
self.statusLabel.setText('Done')
def readResponseHeader(self, responseHeader):
# Check for genuine error conditions.
if responseHeader.statusCode() not in (200, 300, 301, 302, 303, 307):
QMessageBox.information(self, 'Error',
'Download failed: %s.' % responseHeader.reasonPhrase())
self.httpRequestAborted = True
self.http.abort()
def updateDataReadProgress(self, bytesRead, totalBytes):
if self.httpRequestAborted:
return
self.progressBar.setMaximum(totalBytes)
self.progressBar.setValue(bytesRead)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
downloader = Downloader()
downloader.show()
sys.exit(app.exec_())
I suggest you to use the QNetworkManager instead and monitor the download process.
See this other question:
pyQT QNetworkManager and ProgressBars

Categories