Creating a new Qapplication from Qapplication event loop - python

I have rewritten the question so that it is more clear.
In my code I created a QApplication, connected a slot to the application using QTimer.singleShot(), then executed my application.
Now in this slot I want to create another QApplication in another process, I used multiprocessing.Process Class and from inside the process I try to start another QApplication and execute it, but I have an error because an event loop is already running!, I know I can't run two event loops but I am running the new QApplication in another process so it should run.
I know this is not a common implementation but it would be much easier to get this running in my case.
Here is a code example:
The error I get is "QCoreApplication::exec: The event loop is already running"
import multiprocessing
from PyQt4 import QtCore,QtGui
def first_app_slot():
mProcess = multiprocessing.Process(target = run_another_app)
mProcess.start()
mProcess.join()
def run_another_app():
second_app = QtGui.QApplication([])
second_app.exec_()
if __name__ == "__main__":
first_app = QtGui.QApplication([])
QtCore.QTimer.singleShot(0,first_app_slot)
first_app.exec_()

A few problems
In your code, you're not calling any of the multiprocessing code (maybe a typo)?
Don't create the first QApplication in the global scope, put it inside a function. Before creating a new process, multiprocessing will copy the global state to the new process, which includes first_app in this case.
Ex.
def main():
first_app = QtGui.QApplication(sys.argv)
...
if __name__ == '__main__':
main()

Related

How do I do multithreading correctly in PyQt5 while reading and writing to a file simultaneously?

In one window, I have a button that, when clicked, I want to execute a method from another module. This method takes an indeterminate amount of time to execute and depends on user input in the terminal. This method creates a file and repeatedly opens it, writes things to the file, and closes the file. At the same time this is running I have a matplotlib graph widget in the window with a plot that I want to update each time something new is written to the file by reading and plotting data from the most recent line of the file.
To check for changes to the file, I'm using QFileSystemWatcher. Right now, nothing happens while the userInputFunction() is running, but when it finishes I get "data/runName_Rec.txt dataFileCreated". If I then manually edit the file in any way the plotting happens as it should. So it seems the watcher only starts working again and sees that there has been a change in the directory after the userInputFunction() finishes.
How do I do the multithreading correctly so that the watcher works while userInputFunction() is running?
As I understand it, nothing in my application will respond until the user input function finishes if I have it running in the main thread of my QT program. To address this I tried moving the execution of the user input method into a worker thread following the example here: https://realpython.com/python-pyqt-qthread/. In this worker I have two methods. One simply does a for loop with a sleep() that takes a while exactly like the example. The other runs my userInputFunction(). The for loop method, run(), does not freeze the GUI. However, runScan() which exicutes the actual process I want does still freeze the GUI. I'm not sure what is going on here. I'm not sure if this means I'm not doing the threading properly or if something else is going on.
Here is a simplified sample of the relevant parts of my code.
from PyQt5 import QtWidgets, uic, QtCore, QtGui
from pyqtgraph import PlotWidget
from PyQt5.QtCore import QObject, QThread, pyqtSignal
import pyqtgraph as pg
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QInputDialog, QLineEdit, QFileDialog, QMainWindow
import os
from os.path import exists
import csv
import numpy as np
import pandas as pd
import myModule
dirname = os.path.dirname(__file__)
# Create a worker class
class Worker(QObject):
finished = pyqtSignal()
#This works without freezing the GUI
def run(self):
"""Long-running task."""
for i in range(5):
time.sleep(1)
print("step ",i," done!")
self.finished.emit()
#This freezes the GUI
def runScan(self,param1,param2):
"""Long-running task with user input from terminal."""
myModule.userInputFunction(param1,param2)
self.finished.emit()
class someWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(someWindow, self).__init__(*args, **kwargs)
#Load the UI Page
uic.loadUi('somewindow.ui', self)
self.directoryPath = "data"
self.fs_watcher = QtCore.QFileSystemWatcher()
self.fs_watcher.addPath(self.directoryPath)
self.fs_watcher.directoryChanged.connect(self.dataFileCreated)
self.StartScanButton.clicked.connect(self.runLongTask)
self.EndScanButton.clicked.connect(self.endScan)
def dataFileCreated(self):
self.filePath = os.path.join(dirname, "data/"+ self.runNameBox.toPlainText()+"_Rec.txt")
print(self.filePath + "dataFileCreated")
self.fs_watcher.addPath(self.filePath)
self.fs_watcher.fileChanged.connect(self.update_graph)
def update_graph(self):
if exists(self.path):
print("file exists!")
#then read the filePath.txt and plots the data
else:
print("file doesn't exist yet")
def endScan(self):
#change some display things
def runLongTask(self):
# Create a QThread object
self.thread = QThread()
# Create a worker object
self.worker = Worker()
# Move worker to the thread
self.worker.moveToThread(self.thread)
# Connect signals and slots
#This results in the GUI freezing
self.thread.started.connect(self.worker.runScan(self.param1,self.param2))
#This DOES NOT result in the GUI freezing
#self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
# Start the thread
self.thread.start()
# Final resets
self.thread.finished.connect(
lambda: print("long task done!")
)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = someWindow()
w.show()
sys.exit(app.exec_())
The GUI freezes in your case because of this line:
self.thread.started.connect(self.worker.runScan(self.param1,self.param2))
This causes runScan to be executed from the main thread and blocking anything until completed, including the connect.
It's also a serious error, as connect always expects a callable as argument, and as soon as runScan eventually completes its job it returns None and your program will crash.
Assuming that the parameters are being added when the thread is created, you could add those to the Worker constructor and then execute the required code in run:
class Worker(QObject):
finished = pyqtSignal()
def __init__(self, param1, param2):
super().__init__()
self.param1 = param1
self.param2 = param2
def run(self):
myModule.userInputFunction(self.param1, self.param2)
self.finished.emit()
class someWindow(QtWidgets.QMainWindow):
# ...
def runLongTask(self):
self.thread = QThread()
self.worker = Worker(self.param1, self.param2)
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
Note that it's not like the QFileSystemWatcher "stops" while processing: the problem is that by running runScan in the main thread you completely block the main thread (not only the UI), preventing the watcher to handle the events that the OS sends it to notify about the changes.

QThread Windows not responding

I wrote a GUI program in PyQt on Windows. There's some expensive operations in my program. While these operations are running, the program shows "Not Responding" in the program bar.
I think it must be this operation block the main thread to update the UI, so I write multi-threading code by QThread to test it, it still not make sense.
I wrote a small program to test it, the operation did not run in new thread at all, here is my small test code:
from PyQt5.QtCore import QThread, QObject, QCoreApplication, qDebug, QTimer
class Worker(QObject):
def on_timeout(self):
qDebug('Worker.on_timeout get called from: %s' % hex(int(QThread.currentThreadId())))
if __name__ == '__main__':
import sys
app = QCoreApplication(sys.argv)
qDebug('From main thread: %s' % hex(int(QThread.currentThreadId())))
t = QThread()
qDebug(t)
worker = Worker()
timer = QTimer()
timer.timeout.connect(worker.on_timeout)
timer.start(1000)
timer.moveToThread(t)
worker.moveToThread(t)
t.start()
app.exec_()
Here is the output:
From main thread: 0x634
Worker.on_timeout get called from: 0x634
Your program has several errors and does not produce the output that you show.
Firstly, it is not possible to pass a thread object to qDebug - the argument must be a string. If you want to print objects, use qDebug(repr(obj)) - or even better, just use print(obj).
Secondly, you cannot start a timer outside of the thread that created it. Your example makes the signal connection in the main thread, and starts the timer in the main thread as well. So worker.on_timeout will be called in the main thread. But if you connect and start the timer after moving it to the worker thread, you will get this error:
QObject::startTimer: Timers can only be used with threads started with
QThread
I think using a timer is unnecessary, and confuses your example, so it is better to leave it out altogether. Instead, you should connect the started signal of the worker thread to a run method of the worker object. To simulate a long-running operation, you can use QThread.sleep():
from PyQt5.QtCore import QThread, QObject, QCoreApplication
class Worker(QObject):
def run(self):
print('Worker called from: %#x' % int(QThread.currentThreadId()))
QThread.sleep(2)
print('Finished')
QCoreApplication.quit()
if __name__ == '__main__':
import sys
app = QCoreApplication(sys.argv)
print('From main thread: %#x' % int(QThread.currentThreadId()))
t = QThread()
worker = Worker()
worker.moveToThread(t)
t.started.connect(worker.run)
t.start()
app.exec_()
Finally, note that you should always make signal connections after moving a worker object to a thread. The reasons for this are explained in this answer.

Send value to worker thread PyQt?

I'm just learning to program GUI's with PyQt. The GUI that I'm trying to make sends a value too an Arduino with the Pyserial library. I got it working but after a few sends the GUI freezes.
I read somewhere that threading can solve this, so I tried this but I can't figure out how to send a value too the working thread from the main thread. For example I want to send the number 123 too the working thread. How do I go about this?
You can pass a parameter to a QThread when initializing it, like this:
import sys
from PyQt4 import QtGui, QtCore
class FooThread(QtCore.QThread):
def __init__(self, foo):
super(FooThread, self).__init__()
self.foo = foo
def run(self):
print self.foo
def main():
app = QtGui.QApplication(sys.argv)
foo_thread = FooThread('foo')
foo_thread.start()
foo_thread.finished.connect(QtGui.qApp.exit)
sys.exit(app.exec_())
if __name__ == '__main__':
main()
This snippet will create a PyQt application and then starts a thread that prints out the parameter that is passed to its constructor (in the example above it prints foo), then terminates the execution of the application.
Hope it helps!

QT threading. Using pyqt to setup the QtHelpEngine

I'd appreciate some help on using the QtHelpEngine. I've tried a few approaches with various results. Below is the code that seems most intuitive to me but I run in to threading problems. The problem I'm having now is to use QT signals to perform the necessary actions in the correct sequence.
The error I get is this:
QObject: Cannot create children for a parent that is in a different
thread. (Parent is QHelpEngine(0x226f6143780), parent's thread is
QmlSearch(0x226f61382a0), current thread is QThread(0x226f61bda80)
I'd appreciate if someone could give me some advice on how to solve this or how the help engine is intended to be used.
Thanks!
from PyQt5 import QtHelp, QtCore
import sys
import time
class QmlSearch(QtCore.QThread):
def run(self):
# setup help engine
qhcPath = 'C:/Users/jonoa/Documents/TEST.qhc' # just a temporary test file
self.qmlHelp = QtHelp.QHelpEngine(qhcPath)
self.qmlHelp.setupFinished.connect(self.onSetupFinished)
self.qmlHelp.setupData()
def onSetupFinished(self):
print('setup finished')
# when setup is finished register documentation
path2 = 'C:/Program Files/QT/Docs/Qt-5.7/qtquick.qch' # an example test file
self.qmlHelp.registerDocumentation(path2)
# Then setup the search engine
self.qmlSearch = self.qmlHelp.searchEngine() # This is where the script breaks.
self.qmlSearch.reindexDocumentation()
def onIndexFinished(self):
print('indexing finished')
# create search query and perform search
query = QtHelp.QHelpSearchQuery(0, ['rectangle'])
self.qmlSearch.searchingFinished.connect(self.onSearchFinished)
self.qmlSearch.search([query])
def onSearchFinished(self):
print('search finished')
# print some of the hits and finish
print([a for a in self.qmlSearch.hits(0, self.qmlSearch.hitCount()) if 'rectangle' in a[1].lower()])
self.finished.emit()
if __name__ == '__main__':
app = QtCore.QCoreApplication(sys.argv)
thread = QmlSearch()
thread.finished.connect(app.exit)
thread.start()
sys.exit(app.exec_())

How to avoid the warning “QApplication was not created in main() thread” without having access to the main thread?

I wrote code in PyQt4 that scrapes a website and its inner frames.
import sys, signal
from PyQt4 import QtGui, QtCore, QtWebKit
class Sp():
def save(self, ok, frame=None):
if frame is None:
print ('main-frame')
frame = self.webView.page().mainFrame()
else:
print('child-frame')
print('URL: %s' % frame.baseUrl().toString())
print('METADATA: %s' % frame.metaData())
print('TAG: %s' % frame.documentElement().tagName())
print('HTML: ' + frame.toHtml())
print()
def handleFrameCreated(self, frame):
frame.loadFinished.connect(lambda: self.save(True, frame=frame))
def main(self):
self.webView = QtWebKit.QWebView()
self.webView.page().frameCreated.connect(self.handleFrameCreated)
self.webView.page().mainFrame().loadFinished.connect(self.save)
self.webView.load(QtCore.QUrl("http://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe_scrolling"))
signal.signal(signal.SIGINT, signal.SIG_DFL)
print('Press Crtl+C to quit\n')
app = QtGui.QApplication(sys.argv)
s = Sp()
s.main()
sys.exit(app.exec_())
This code depends on creating an instance of QApplication and exiting it accordingly.
The problem with this is that QApplication must be created and exited in the main thread.
I don't have access to the main thread in the project that i'm developing.
Is it possible to avoid the error “QApplication was not created in main() thread” in some way?
Maybe by rewriting the code for it to work without QApplication or somehow make QApplication work without the main thread?
Edit: I can edit the main thread if it doesn't intervene with its flow of the execution of its code, for example app = QtGui.QApplication([]) wouldn't stop the flow but a function that hangs until some code in another thread would finish would be considered inapplicable.

Categories

Resources