How to run a PyQt app with connexion module? - python

I want to run a connexion server in a Qt app, but i don't know how doin this.
I've tried stuff like below, but execution is stuck in "connexion loop" and button "close server" won't show unit i ctrl-c connexion server in console... :
import sys, os
import connexion
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QPushButton
connex_app = connexion.App("Hello World")
class OpennoteserverDlg(QPushButton):
def __init__(self, parent=None):
super().__init__(
"&Close Server", parent)
self.clicked.connect(self.close)
self.setWindowTitle("Opennote-server")
app = QApplication(sys.argv)
form = OpennoteserverDlg()
form.show()
app.connex_app = connex_app
app.connex_app.run()
app.exec_()

The run() method is blocking, so it will not allow the GUI event loop to be executed, causing several GUI tasks not to work correctly. The solution is to run the server in another thread
import signal
import sys
import threading
import connexion
from PyQt5.QtWidgets import QApplication, QPushButton
class OpennoteserverDlg(QPushButton):
def __init__(self, parent=None):
super().__init__("&Close Server", parent)
self.clicked.connect(self.close)
self.setWindowTitle("Opennote-server")
def run_server():
connex_app = connexion.App("Hello World")
connex_app.run()
if __name__ == "__main__":
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QApplication(sys.argv)
form = OpennoteserverDlg()
form.show()
threading.Thread(target=run_server, daemon=True).start()
sys.exit(app.exec_())

Related

Restarting PyQt app in PyCharm with QProcess

Button click starts a new detached process of the same app. However, when launched from PyCharm, it crashes on second restart. Print is no longer visible after restart as well.
I want my app to be able to restart many times when launched from either PyCharm or cmd.
import sys, os
from PyQt5.QtCore import QProcess
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
button = QPushButton("Restart")
button.clicked.connect(self.restart)
self.setCentralWidget(button)
def restart(self):
print("Restart")
self.close()
proc = QProcess()
proc.startDetached(sys.executable, sys.argv)
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
if __name__ == "__main__":
main()

Script is still running after closing pyqt5 window

import sys
import requests
from PyQt5.uic import loadUi
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QDialog, QApplication
class MainWindow(QDialog):
def __init__(self):
super(MainWindow, self).__init__()
loadUi("tabletutorial.ui",self)
self.request_function()
def request_function(self):
for i in range(0,100):
resp=requests.get("https://www.google.com")
print(resp.status_code)
# main
app = QApplication(sys.argv)
mainwindow = MainWindow()
widget = QtWidgets.QStackedWidget()
widget.addWidget(mainwindow)
widget.setFixedHeight(850)
widget.setFixedWidth(1120)
widget.show()
try:
sys.exit(app.exec_())
except:
print("Exiting")
This window is created by another main window.
Now the problem is when i quit the pyqt5 window the script is still running and i am getting the status code .I am running a big application with a bunch of requests.Anyone with relatable solution please ?
I TRIED:
self.close() not worked for me. #QtCore.pyqtSlot() also not worked.
I am new to here . Please ignore mistakes and kind answer are appriciated.

Stop loop in import

I am making a custom console/shell window module using pyqt5 I have basically everything working but when I import it the whole entire script freezes because of a loop in the module that I import.
This is the module that gets imported:
import sys
import time
from PyQt5 import QtWidgets, uic
from PyQt5.QtCore import QRunnable, QThreadPool
from PyQt5.QtWidgets import QTextEdit
class SpamWorker(QRunnable):
def __init__(self, console: QTextEdit):
super().__init__()
self.console = console
def run(self):
while True:
time.sleep(0.2)
self.console.append('SPAM!')
class Printer(QtWidgets.QMainWindow, QRunnable):
def __init__(self):
super(Printer, self).__init__()
uic.loadUi('window.ui', self)
self.setWindowTitle("Console+")
self.thread_pool = QThreadPool()
self.thread_pool.start(SpamWorker(self.Console))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Printer()
window.show()
sys.exit(app.exec_())
how can I import it without it freezing?

How to reboot PyQt5 application

I'm trying to restart my application after an update from the client side. I was able to achieve till the auto update part. I tried to surf on How to restart PyQt application?. There are few similar questions earlier, but none of them have good explanation or example with a button click event. Could you guys help me understand on how to reboot a PyQt application. Basically I want to restart the application from if __name__ == '__main__': everytime there is an update.
Note: AppLogin is my private module I created to handle application login. So basically that would be the landing QDialog once application is opened.
from PyQt5.QtWidgets import *
import sys
import AppLogin
class App:
def __init__(self):
btn = QPushButton(main_window)
btn.setText('close')
btn.pressed.connect(self.restart)
main_window.show()
def restart(self):
# Here goes the code for restart
pass
if __name__ == '__main__':
appctxt = QApplication(sys.argv)
log_in = AppLogin.Login()
if log_in.exec_() == QDialog.Accepted:
main_window = QMainWindow()
ui = App()
exit_code = appctxt.exec_()
sys.exit(exit_code)
The logic is to end the eventloop and launch the application an instant before it closes:
import sys
from PyQt5 import QtCore, QtWidgets
def restart():
QtCore.QCoreApplication.quit()
status = QtCore.QProcess.startDetached(sys.executable, sys.argv)
print(status)
def main():
app = QtWidgets.QApplication(sys.argv)
print("[PID]:", QtCore.QCoreApplication.applicationPid())
window = QtWidgets.QMainWindow()
window.show()
button = QtWidgets.QPushButton("Restart")
button.clicked.connect(restart)
window.setCentralWidget(button)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
A solution is to close (or forget) the QMainWindow and recreate it.
If you just "show()" a single widget, the same idea works fine.
import sys
import uuid
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
singleton: 'MainWindow' = None
def __init__(self):
super().__init__()
btn = QPushButton(f'RESTART\n{uuid.uuid4()}')
btn.clicked.connect(MainWindow.restart)
self.setCentralWidget(btn)
self.show()
#staticmethod
def restart():
MainWindow.singleton = MainWindow()
def main():
app = QApplication([])
MainWindow.restart()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

QtWebPageRenderer SIGILL issue

My problem is summed in title. When I call method setHtml on instance of QtWebPageRenderer, SIGILL signal is emitted and my application goes down.
I'm aware that this issue is caused by bad Qt5 dynamic library but I installed it with:
sudo pip install PyQt5 --only-binary PyQt5
sudo pip install PyQtWebEngine --only-binary PyQtWebEngine
so I thought I will get correct precompiled library. When I tried to install PyQt5 without --only-binary, I always ended with some strange compilation error. Something like qmake is not in PATH even though it is and I'm able to call qmake from shell.
So my question is, how to make PyQt5 running on Fedora 31 without any SIGILLs.
EDIT:
Following code can replicate the issue. That information about SIGILL is little inaccurate because first signal is actually SIGTRAP, after I hit continue in gdb, I got SIGILL. This hints that Qt is actually trying to say something to me, although in not very intuitive way.
After some playing around with it, I found that without thread, its ok. Does this mean that Qt forces user to use QThread and not python threads? Or it means that I can't call methods of Qt objects outside of thread where event loop is running?
import signal
import sys
import threading
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEnginePage
class WebView(QWebEnginePage):
def __init__(self):
QWebEnginePage.__init__(self)
self.loadFinished.connect(self.on_load_finish)
def print_result(self, data):
print("-" * 30)
print(data)
with open("temp.html", "wb") as hndl:
hndl.write(data.encode("utf-8"))
def on_load_finish(self):
self.toHtml(self.print_result)
class Runner(threading.Thread):
def __init__(self, web_view):
self.web_view = web_view
threading.Thread.__init__(self)
self.daemon = True
def run(self):
self.web_view.load(QtCore.QUrl("https://www.worldometers.info/coronavirus/"))
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtWidgets.QApplication(sys.argv)
web_view = WebView()
runner = Runner(web_view)
runner.start()
app.exec_()
if __name__ == "__main__":
main()
You have to have several restrictions:
A QObject is not thread-safe so when creating "web_view" in the main thread then it is not safe to modify it in the secondary thread
Since the QWebEnginePage tasks run asynchronously then you need a Qt eventloop.
So if you want to use python's Thread class then you must implement both conditions:
import signal
import sys
import threading
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEnginePage
class WebView(QWebEnginePage):
def __init__(self):
QWebEnginePage.__init__(self)
self.loadFinished.connect(self.on_load_finish)
def print_result(self, data):
print("-" * 30)
print(data)
with open("temp.html", "wb") as hndl:
hndl.write(data.encode("utf-8"))
def on_load_finish(self):
self.toHtml(self.print_result)
class Runner(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.daemon = True
def run(self):
# The QWebEnginePage was created in a new thread and
# that thread has an eventloop
loop = QtCore.QEventLoop()
web_view = WebView()
web_view.load(QtCore.QUrl("https://www.worldometers.info/coronavirus/"))
loop.exec_()
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtWidgets.QApplication(sys.argv)
runner = Runner()
runner.start()
app.exec_()
if __name__ == "__main__":
main()
In reality QThread and threading.Thread() are native thread handlers of the OS, so in practical terms it can be said that QThread is a threading.Thread() + QObject with an eventloop running on the secondary thread.
On the other hand, if your objective is to call a function from a thread to which it does not belong, then you should use asynchronous methods as pointed out in this answer.
In this case the simplest is to use pyqtSlot + QMetaObject:
import signal
import sys
import threading
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEnginePage
class WebView(QWebEnginePage):
def __init__(self):
QWebEnginePage.__init__(self)
self.loadFinished.connect(self.on_load_finish)
def print_result(self, data):
print("-" * 30)
print(data)
with open("temp.html", "wb") as hndl:
hndl.write(data.encode("utf-8"))
def on_load_finish(self):
self.toHtml(self.print_result)
#QtCore.pyqtSlot(QtCore.QUrl)
def load(self, url):
QWebEnginePage.load(self, url)
class Runner(threading.Thread):
def __init__(self, web_view):
self.web_view = web_view
threading.Thread.__init__(self)
self.daemon = True
def run(self):
url = QtCore.QUrl("https://www.worldometers.info/coronavirus/")
QtCore.QMetaObject.invokeMethod(
self.web_view,
"load",
QtCore.Qt.QueuedConnection,
QtCore.Q_ARG(QtCore.QUrl, url),
)
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtWidgets.QApplication(sys.argv)
web_view = WebView()
runner = Runner(web_view)
runner.start()
app.exec_()
if __name__ == "__main__":
main()
Or functools.partial() + QTimer
from functools import partial
import signal
import sys
import threading
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEnginePage
class WebView(QWebEnginePage):
def __init__(self):
QWebEnginePage.__init__(self)
self.loadFinished.connect(self.on_load_finish)
def print_result(self, data):
print("-" * 30)
print(data)
with open("temp.html", "wb") as hndl:
hndl.write(data.encode("utf-8"))
def on_load_finish(self):
self.toHtml(self.print_result)
class Runner(threading.Thread):
def __init__(self, web_view):
self.web_view = web_view
threading.Thread.__init__(self)
self.daemon = True
def run(self):
wrapper = partial(
self.web_view.load,
QtCore.QUrl("https://www.worldometers.info/coronavirus/"),
)
QtCore.QTimer.singleShot(0, wrapper)
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtWidgets.QApplication(sys.argv)
web_view = WebView()
runner = Runner(web_view)
runner.start()
app.exec_()
if __name__ == "__main__":
main()

Categories