Pyside qwebview custom protocol - pyside

I'd like to support custom protocol inside my pyside app but without success. So far I tried:
class MainWindow(QWebView):
def __init__(self, parent=None):
oldManager = self.page().networkAccessManager()
self.page().setNetworkAccessManager(NetworkAccessManager(self, oldManager))
#in another file
class NetworkAccessManager(QNetworkAccessManager):
def __init__(self, parent, oldManager):
QNetworkAccessManager.__init__(self)
self.oldManager = oldManager
self.setCache(oldManager.cache())
self.setCookieJar(oldManager.cookieJar())
self.setProxy(oldManager.proxy())
self.setProxyFactory(oldManager.proxyFactory())
print('There')
def createRequest(self, operation, request, data):
print('And there')
This results in a segmentation faultunder windows. I saw this :
It is currently not supported to change the network access manager after the PySide.QtWebKit.QWebPage has used it.
But I don't see where it would be used in this case. I tried to set a web page object after setting the network manager and
the segmentation fault disappeared.
PS: none of the print statement is displayed inside the console.

If createRequest doesn't return a reply it crahes. So the complete solution is:
class MainWindow(QWebView):
def __init__(self, parent=None):
oldManager = self.page().networkAccessManager()
self.setPage(DebugWebPage()) #if you want to set a custom page
self.page().setNetworkAccessManager(NetworkAccessManager(self))
class NetworkAccessManager(QNetworkAccessManager):
def __init__(self, parent):
QNetworkAccessManager.__init__(self)
def createRequest(self, operation, request, data):
if request.url().scheme() != 'page':
return QNetworkAccessManager.createRequest(self, operation, request, data)
if operation == self.GetOperation:
# Handle page:// URLs separately by creating custom
# QNetworkReply objects.
reply = PageReply(self, request.url(), self.GetOperation)
print('here')
return reply
else:
return QNetworkAccessManager.createRequest(self, operation, request, data)
class PageReply(QNetworkReply):
def __init__(self, parent, url, operation):
QNetworkReply.__init__(self, parent)
self.content = '<html><head><title>Test</title></head><body>This is a test.</body></html>'
self.offset = 0
self.setHeader(QNetworkRequest.ContentTypeHeader, 'text/html; charset=utf-8')
self.setHeader(QNetworkRequest.ContentLengthHeader, len(self.content))
QTimer.singleShot(0, self, SIGNAL('readyRead()'))
QTimer.singleShot(0, self, SIGNAL('finished()'))
self.open(self.ReadOnly | self.Unbuffered)
self.setUrl(url)
def abort(self):
pass
def bytesAvailable(self):
return len(self.content) - self.offset + QNetworkReply.bytesAvailable(self)
def isSequential(self):
return True
def readData(self, maxSize):
if self.offset < len(self.content):
end = min(self.offset + maxSize, len(self.content))
data = self.content[self.offset:end]
self.offset = end
return data
Note: I don't really know why but any error while the script is in the network manager or the reply results in a segmentation fault.
Based on this with some correction.

Related

Why using "fork" works but using "spawn" fails in Python3.8+ `multiprocessing`?

I work on macOS and lately got bitten by the "fork" to "spawn" change in Python 3.8 multiprocessing (see doc). Below shows a simplified working example where using "fork" succeeds but using "spawn" fails. The purpose of the code is to create a custom queue object that supports calling size() under macOS, hence the inheritance from the Queue object and getting multiprocessing's context.
import multiprocessing
from multiprocessing import Process
from multiprocessing.queues import Queue
from time import sleep
class Q(Queue):
def __init__(self):
super().__init__(ctx=multiprocessing.get_context())
self.size = 1
def call(self):
return print(self.size)
def foo(q):
q.call()
if __name__ == '__main__':
multiprocessing.set_start_method('spawn') # this would fail
# multiprocessing.set_start_method('fork') # this would succeed
q = Q()
p = Process(target=foo, args=(q,))
p.start()
p.join(timeout=1)
The error message output when using "spawn" is shown below.
Process Process-1:
Traceback (most recent call last):
File "/usr/local/Cellar/python#3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
self.run()
File "/usr/local/Cellar/python#3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "/Users/fanchen/Private/python_work/sandbox.py", line 23, in foo
q.call()
File "/Users/fanchen/Private/python_work/sandbox.py", line 19, in call
return print(self.size)
AttributeError: 'Q' object has no attribute 'size'
It seems that the child process deems self.size not necessary for code execution, so it is not copied. My question is why does this happen?
Code snippet tested under macOS Catalina 10.15.6, Python 3.8.5
The problem is that spawned processes do not have shared resources, so to properly recreate the queue instance for each process you need to add serialization and deserialization methods.
Here is a working code:
# Portable queue
# The idea of Victor Terron used in Lemon project (https://github.com/vterron/lemon/blob/master/util/queue.py).
# Pickling/unpickling methods are added to share Queue instance between processes correctly.
import multiprocessing
import multiprocessing.queues
class SharedCounter(object):
""" A synchronized shared counter.
The locking done by multiprocessing.Value ensures that only a single
process or thread may read or write the in-memory ctypes object. However,
in order to do n += 1, Python performs a read followed by a write, so a
second process may read the old value before the new one is written by the
first process. The solution is to use a multiprocessing.Lock to guarantee
the atomicity of the modifications to Value.
This class comes almost entirely from Eli Bendersky's blog:
http://eli.thegreenplace.net/2012/01/04/shared-counter-with-pythons-multiprocessing/
"""
def __init__(self, n = 0):
self.count = multiprocessing.Value('i', n)
def __getstate__(self):
return (self.count,)
def __setstate__(self, state):
(self.count,) = state
def increment(self, n = 1):
""" Increment the counter by n (default = 1) """
with self.count.get_lock():
self.count.value += n
#property
def value(self):
""" Return the value of the counter """
return self.count.value
class Queue(multiprocessing.queues.Queue):
""" A portable implementation of multiprocessing.Queue.
Because of multithreading / multiprocessing semantics, Queue.qsize() may
raise the NotImplementedError exception on Unix platforms like Mac OS X
where sem_getvalue() is not implemented. This subclass addresses this
problem by using a synchronized shared counter (initialized to zero) and
increasing / decreasing its value every time the put() and get() methods
are called, respectively. This not only prevents NotImplementedError from
being raised, but also allows us to implement a reliable version of both
qsize() and empty().
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs, ctx=multiprocessing.get_context())
self._counter = SharedCounter(0)
def __getstate__(self):
return super().__getstate__() + (self._counter,)
def __setstate__(self, state):
super().__setstate__(state[:-1])
self._counter = state[-1]
def put(self, *args, **kwargs):
super().put(*args, **kwargs)
self._counter.increment(1)
def get(self, *args, **kwargs):
item = super().get(*args, **kwargs)
self._counter.increment(-1)
return item
def qsize(self):
""" Reliable implementation of multiprocessing.Queue.qsize() """
return self._counter.value
def empty(self):
""" Reliable implementation of multiprocessing.Queue.empty() """
return not self.qsize()
You can also use multiprocessing.manager.Queue

How can I add websockets and handlers to a running tornado server?

I'm trying to build a tornado application, where clients can register themselfes through a request to communicator websocket.
When the communicator receives a request to create a new handler and websocket should be added to the server. The new websocket is an intatiation of ClientWebSocketHandler.
How do I use function add_handlers() correctly.
How does the webserver get informed about new connections?
Any ideas what I'm missing here?
Is there a standard way for dynamically creatint more handlers and websockets?
class Communicator(tornado.websocket.WebSocketHandler):
def initialize(self):
print('communicator initialized')
def open(self):
print('communicator ready to receive messages')
def on_message(self, msg):
print('msg received: ' + msg)
self.create_socket()
def on_close(self):
print('communicator closed')
def create_socket(self):
self.application.add_handlers(
host_pattern = r'/',
host_handlers = [
(r'/clients/123', ClientWebsocketHandler),
]
)
class Application(tornado.web.Application):
def __init__(self):
settings = SWIFT_SERVER_SETTINGS
handlers = [
(r'/', IndexPageHandler),
(r'/', tornado.web.StaticFileHandler, dict(path=settings['static_path'])),
# (r'/clients/123', ClientWebsocketHandler),
(r'/com', Communicator),
]
tornado.web.Application.__init__(self, handlers, **settings)
You dont need that function, change your code to this
clients=[]
class Communicator(websocket.WebSocketHandler):
def check_origin(self, origin):
return True
def open(self):
clients.append(self)
def on_message(self, message):
print self,message
#return message
self.write_message(message)
def on_close(self):
clients.remove(self)
I added clients objects to a global variable to later or in another function you can search for the object and write a message to that client.

How to update the Wxpython statusbar with run-time data streaming in another *.py file

my project has many python files,and right now the problem is when I clicked button in Main-UI interface,it will invoke some function in another python file(sub-program),and i need all the running status in sub-program will also be updated in main-UI,how do i accomplish this?
The thing i can try so far i known is using socket,but i want to know do you guys have any other good ideas on this?
The code like this:
1. Main-UI:
import wx,time
from threading import Thread
from path import basicTest
EVT_RESULT_ID = wx.NewId()
def EVT_RESULT(win, func):
win.Connect(-1, -1, EVT_RESULT_ID, func)
class ResultEvent(wx.PyEvent):
def __init__(self, data):
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data
class TestThread(Thread):
def __init__(self, wxObject):
Thread.__init__(self)
self.wxObject = wxObject
self.start()
def run(self):
this masked sub-script can run,but what i want to do is to replace it with invoking from another python file
'''
for i in range(6):
time.sleep(1)
wx.PostEvent(self.wxObject, ResultEvent(i))
'''
data = basicTest().run(10)
wx.PostEvent(self.wxObject, ResultEvent(data))
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Tester")
panel = wx.Panel(self, wx.ID_ANY)
self.btn = wx.Button(panel, label="Start Test")
self.statusbar = self.CreateStatusBar()
self.btn.Bind(wx.EVT_BUTTON, self.onButton)
EVT_RESULT(self, self.updateStatus)
def onButton(self, event):
TestThread(self)
btn = event.GetEventObject()
btn.Disable()
def updateStatus(self, msg):
t = msg.data
self.statusbar.SetStatusText("Sequence %i running.." %t)
self.btn.Enable()
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyForm().Show()
app.MainLoop()
sub-script:
import time
class basicTest():
def run(self,inter):
for i in range(inter):
return i
time.sleep(1)
As list above and also i known that the main UI only updated when the sub-script finished,it's not run-time refresh from another sub,can anyone help me, very appreciate
I would just leave the long running code in its thread. Then you can have wxPython start the thread. In your thread's run method, just use one of wxPython's thread-safe methods to call your UI. I would recommend wx.CallAfter or 'wx.PostEvent'.
Once you have that done, then you just execute the necessary method in your main UI.

How to limit number of Toplevel windows in tkinter

I have a bit of code that creates a a top level window upon the user
pressing a button.
However i would like to limit the number Top level windows to one, so the user couldn't Spam the button and open fifty windows.
import tkinter as tk
class app():
def __init__(self,master):
self.master = master
master.configure(background = '#002e3d')
master.title('Test!')
master.geometry = master.geometry('660x550+200+200')
master.resizable(width = False,height = False)
self.button = tk.Button(master,text = 'Test'command = self.searchmenu)
self.button.pack()
def searchmenu(self):
Demo()
class Demo():
def __init__(self):
self.top = tk.Toplevel()
self.top.title('Search!')
def main():
root = tk.Tk()
window = app(root)
root.mainloop()
If you make a reference to the Demo object you create (which I'd recommend regardless) this becomes very trivial task:
class app():
def __init__(self,master):
...
self.popup = None
def searchmenu(self):
if self.popup is None:
self.popup = Demo()
Although once the created window is destroyed this doesn't allow to reopen it, so you may want to also check if the top still exists with winfo_exists():
def searchmenu(self):
if self.popup is None or not self.popup.top.winfo_exists():
self.popup = Demo()
EDIT: if the popup is already open then pushing the button should lift it to the top of the window stack:
def searchmenu(self):
if self.popup is None or not self.popup.top.winfo_exists():
self.popup = Demo()
else:
self.popup.top.lift(self.master)

How can I update the textctrl content in GUI?

I am trying to write my first wxpython GUI program,in my program,I want to get anothor software window's title,if the title change,clear the old title and show the new title in GUI,I test in cmd,it can get the title in a loop,but I don't konw how to set a event in GUI to update the title.
my code:
def getinfos():
tempWindowName=win32gui.GetWindowText (find_window())
while True:
titles=[]
if (tempWindowName==win32gui.GetWindowText (find_window())):
pass
else:
tempWindowName=win32gui.GetWindowText (find_window())
titles.append(tempWindowName)
return title[0]
time.sleep(1000)
and the GUI code:
import controller2
import time
########################################################################
class InfoPanel(wx.Panel):
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
try:
self.titleResults = controller2.getinfos()
except:
self.titleResults = 'no data'
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.titlecontent = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE|wx.TE_RICH|wx.TE_LEFT|wx.TE_WORDWRAP|wx.NO_BORDER)
self.titlecontent.SetBackgroundColour('white')
self.settitle()
mainSizer.Add(self.yejicontent, 2.5, wx.ALL|wx.EXPAND, 5)
self.SetSizer(mainSizer)
#----------------------------------------------------------------------
def settitle(self):
self.titlecontent.SetValue("%s"%self.titleResults)
########################################################################
class InfoFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="title",size=(500, 880))
panel = InfoPanel(self)
style= self.GetWindowStyle()
self.SetWindowStyle(style|wx.STAY_ON_TOP)
class MyApp(wx.App):
def OnInit(self):
self.infoFrame=InfoFrame()
self.SetTopWindow(self.infoFrame)
self.infoFrame.Show(True)
return True
#----------------------------------------------------------------------
if __name__ == "__main__":
app = MyApp(False)
app.MainLoop()
Thanks for your time and appreciate for any advise.
Put the getinfos function/method into a thread. When the title changes, have the thread use wx.CallAfter or wx.PostEvent (both of which are thread-safe) to tell the GUI to update. If you don't put it into a thread, you're GUI will be very unresponsive.
http://wiki.wxpython.org/LongRunningTasks
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
Pubsub rocks, but won't work in this case if you're running that getinfos function in your wxPython main loop as it will block it. You could use pubsub in the thread in combination with those threadsafe methods I mentioned though.
You can send a custom wx event or setup pubsub.

Resources