Pywebview blocks flask app unless I open two webviews - python

I built a python desktop webapp with a fairly simple flask backend which I'm currently serving simply through Chrome. I'm trying to package it using pywebview, but I ran into an intestesting roadblock.
I have code that looks like this in the main file of my package:
import os
import sys
from flasher import app
import httplib
import webview
import threading
def start_prod_server():
runserver(debug=False, reloader=False)
def start_gui():
webview.create_window("MyAppName", "http://localhost:5000", height=1000)
def runserver(debug=True, reloader=False):
port = int(os.environ.get('PORT', 5000))
url = "http://localhost:{}".format(port)
print("ready!")
app.run(host='::', port=port, debug=debug, use_reloader=reloader)
if __name__ == '__main__':
t = threading.Thread(target=start_prod_server)
t.daemon = True
t.start()
# This never works
threading.Timer(1.5, start_gui).start()
sys.exit()
This fails almost systematically, the window shows up blank and the backend doesn't respond to anything (including requests from a separate desktop browser).
I discovered accidentally that if I start two webview threads, it always works:
if __name__ == '__main__':
t = threading.Thread(target=start_prod_server)
t.daemon = True
t.start()
# This works every time
threading.Timer(1.5, start_gui).start()
threading.Timer(1.5, start_gui).start()
sys.exit()
What could be causing this? My understanding of threading in Python is fairly limited, so I'm not sure where to look. Is this likely to be a bug in pywebview, or am I doing something wrong with my threads?
I'm also open to suggestions of alternatives for the webview part, but I want to keep the python/flask part since the app already works fine as it is.

Well, after a bit of digging, I think I found the solution myself.
For whatever reason, webview.create_window() wasn't navigating to the page and getting stuck. Opening up the second window was causing both windows to navigate to the url and allowing the app to continue. (I still haven't understood why)
The problem is solved simply by adding webview.load_url("http://localhost:5000") like so:
if __name__ == '__main__':
t = threading.Thread(target=start_prod_server)
t.daemon = True
t.start()
threading.Timer(1.5, start_gui).start()
webview.load_url("http://localhost:5000")
sys.exit()
Any insight as to why this happens is still welcome though... In the source for pywebview, the two methods are making the exact same call to self.browser.web_browser.Navigate(url)

Related

Python process locks main program

So, I create a small program that uses flask to receive some requests and do a few things over selenium. All bits that deal with selenium are in another file that I tried to run first using a thread, and when it did not worked, a process. I believe that the problem is because I use a while true to keep my selenium working. The selenium part knows what to do because it keep checking the variable that I update from them flask part...
This is pretty much my main class that runs the selenium and them start flask, but it never start flask. It get locked on the .start().
if __name__ == "__main__":
# Logging
log_format = '%(asctime)s [%(filename)s:%(lineno)d] %(message)s'
logging.basicConfig(format=log_format,
level=logging.INFO,
stream=sys.stdout)
# Start Selenium
browser = Process(target=selenium_file.run_stuff())
browser.start()
print('TEST')
# Flask
app.run(debug=True)
Not really sure how I could solve this problem (if it's a problem)...
Exchange browser = Process(target=selenium_file.run_stuff()) with browser = Process(target=selenium_file.run_stuff)
You don't pass the function run_stuff but you already execute it and hence it blocks your program until run_stuff returns.

How can I access a running thread

By starting the main, I´m starting a thread that keeps a connection to a opcua server alive (and a few things more).
I now want to open a function inside this thread but I don´t want to import everything again (because it takes to long).
In if __name__ == "__main__":
it is working, but when I run a second script goIntoThread.py, it is not working. Obviously because I didn´t import the modules...
What are my options to trigger e.g. thd.doSomethingInThread() without importing everything again?
Thnaks alot!
main.py
import time
def importOnlyMain():
global KeepConnected
from keepConnected import KeepConnected
if __name__ == "__main__":
importOnlyMain()
global thd
thd = KeepConnected()
thd.start()
time.sleep(3)
thd.doSomethingInThread()
def goIntoThread():
print("Going to Thread")
thd.doSomethingInThread()
goIntoThread.py
import main
main.goIntoThread()
Copy Comment: I get the following error:
thd.setBool()
NameError: global name 'thd' is not defined

is there any way to run bottle application in daemon mode

I have a web application built on bottle(python) frame work and I want to run it in daemon mode.Is there any way to run it in daemon mode
Thanks
Sure you can. Install BottleDaemon 0.1.0 on your OS and than change your router file like so:
from bottledaemon import daemon_run
from bottle import route
#route("/hello")
def hello():
return "Hello World"
# The following lines will call the BottleDaemon script and launch a daemon in the background.
if __name__ == "__main__":
daemon_run()

main function in django web service

I am preparing a web service using django in python. When I start web service, I would like to have a main function to run. However, code below did not work in web service. I want to have an initializer function which will fill the structures defined global. I can achieve it by directly defining an init function and call it end of the module but I don't know whether this is the proper way of doing so.
if __name__ == '__main__':
main()
Here similar question was posted. As I understand there is no clear solution for that and I find my way to do this. I defined a dummy function in web service module like:
def warmup(request):
response = HttpResponse()
response.write("ok")
return response
We start application with command sudo python manage.py runserver 0.0.0.0:8081 so I add a piece of code in manage.py such that:
#!/usr/bin/env python
import os
import sys
import requests
import threading
import time
def warmup_request():
print "warmup.."
time.sleep(1)
try:
r = requests.get("http://localhost:8081/warmup/")
if r.content == "ok":
return
except Exception, e:
pass
threading.Timer(0,warmup_request).start()
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
from django.core.management import execute_from_command_line
warmup_request()
execute_from_command_line(sys.argv)
If warmup service API returns ok it means loading initial data process has finished. By defining a global flag, we can block all other service API requests before warming up has finished. Or instead defining global flag we may use request_started signal stated here to block all incoming requests with more little effort.

QApplication (PySide) Django app issue with quitting

I have a Django app in which the users can create reports at various places throughout the webapp. As a little nifty feature I would like to make a "send as PDF" feature for these reports, and I'm almost there.
What I do is that before the report is returned as a HttpResponse through Django I send the raw HTML content through a small PySide/QT snippet (as seen below).
The problem is that I can't get the QApplication to quit.
I have tried with the standard QCoreApplication.exit() but with no luck. If I try to convert a new report right after the first one, the console says a "QApplication instance already exists".
I am using Django 1.2.5, Python 2.7, QT 4.8 and PySide 1.1 on OS X 10.7.3 (for testing).
Code:
def makepdf(response,filename):
try:
app = QApplication(sys.argv)
except:
app = QCoreApplication.instance()
web = QWebView()
stylelink = "%s%s" % (media_root,'images/css/reportGenerator.css')
web.settings().setUserStyleSheetUrl(QUrl.fromLocalFile(stylelink))
web.setHtml(response)
printer = QPrinter()
printer.setPageSize(QPrinter.A4)
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName(filename)
def convertToPdf():
web.print_(printer)
#webresponse = web.close()
#QObject.disconnect()
#print QCoreApplication.instance().children()[0].interrupt()
#qthread = QCoreApplication.instance().thread()#.cleanup()
#qthread.exit()
QCoreApplication.exit()
QObject.connect(web, SIGNAL("loadFinished(bool)"), convertToPdf())
Code comments:
Currently I have made a try/except clause to be able to keep the code running, by using the current QApplication instance (to avoid the 'instance exists error'), but this just doesn't seem right?. I mean, having a QAppliation running for the duration of my Apache server (which will be the case in production) seems a bit off. Shouldn't it be possible to quit it after the PDF conversion is done?
Other than the QCoreApplication.exit() I have tried to use the sys.exit(app.exec_()) method, as is often seen in examples and snippets. This however just causes an error and makes Python crash and the code works perfectly without this. (well it creates the PDF, but won't quit).
All the lines commented out are the previous attempts I have made to quit the QApplication.
In short: I don't know what it is, but it just won't quit. Can anyone suggest why?
Update: After the latest answer I have edited the code to respond to the input. This is how the final part looks now:
def convertToPdf():
web.print_(printer)
app.exit()
web.loadFinished.connect(convertToPdf)
app.exec_()
I do, however, still get an error:
2012-04-30 00:16:10.791 Python[21241:1803] * Assertion failure in +[NSUndoManager _endTopLevelGroupings], /SourceCache/Foundation/Foundation-833.24/Misc.subproj/NSUndoManager.m:324
Qt has caught an exception thrown from an event handler. Throwing exceptions from an event handler is not supported in Qt. You must reimplement QApplication::notify() and catch all exceptions there.
This error only occurs when I implement app.exec_(). However without app.exec_() it is just the normal issue again, with no quitting.
Any ideas?
Update 2: this is the newest code, fixed in accordance with matas latest suggestion:
app = QApplication(sys.argv)
web = QWebView()
printer = QPrinter()
printer.setPageSize(QPrinter.A4)
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName(filename)
def convertToPdf():
web.print_(printer)
app.exit()
web.loadFinished.connect(convertToPdf)
web.setHtml(response)
I still have the same problem however.
what i can say is this:
QObject.connect(web, SIGNAL("loadFinished(bool)"), convertToPdf())
here you call convertToPdf(), if you want to connect the signal, omit the parenthesis!
you could also use this much clearer syntax:
web.loadFinished.connect(convertToPdf)
you may also want to add a parameter to convertToPdf, as it is called with a boolean indicating wheather loading was successful or not.
And using app.exit() should be enough.
oh, and when you use Gui-Components you need to use a QApplication. A QCoreApplication won't do!
edit: it's important to call web.setHtml after you've connected the loadFinished signal! otherwise if the loading already is finished, your function will never be executed!
edit: this works without any problem for me:
#!/usr/bin/env python
from PySide.QtGui import *
from PySide.QtWebKit import *
import sys
from subprocess import Popen, PIPE
def createPdf(html, filename):
app = QApplication(sys.argv)
web = QWebView()
printer = QPrinter()
printer.setPageSize(QPrinter.A4)
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName(filename)
def convertToPdf():
web.print_(printer)
app.exit()
app.deleteLater()
web.loadFinished.connect(convertToPdf)
web.setHtml(html)
app.processEvents()
def createPdfInSubprocess(html, filename):
p = Popen(["python", __file__, filename],
stdin=PIPE, stdout=PIPE, stderr=PIPE)
out, err = p.communicate(html)
return (p.returncode, out, err)
if __name__ == '__main__':
if len(sys.argv) > 1:
# read html from stdin, filename from cmdline
html = "\n".join(sys.stdin.readlines())
createPdf(html, sys.argv[1])
# print("done")
else:
# test if it's working
ret = createPdfInSubprocess(
"<html><h1>test</h1>it's working...</html>", "test.pdf")
print(ret)
Even without all the calls to app.exit(), app.deleteLater(), app.processEvents(), it still works... but can't hurt to have that.
One more important thing: QApplications have to be created from the main thread! So if it's running inside a django app it's probably not going to work, that's why i added the subprocess stuff...
PySide is designed such that there is only ever one instance of QCoreApplication within a process. The best reference to this (possibly undocumented fact) that I could find was http://bugs.pyside.org/show_bug.cgi?id=855.
Basically, there may be dangling references to the qapplication that prevent it from being reaped by the garbage collector, so even if you tell the application to exit and you delete your reference, there may still be other references lying around.

Categories