QtReactor Closes Application Window (Using Scrapy) - pyqt

I have setup a basic QT (PyQt4) application. It runs a couple of spiders using Scrapy and to avoid blocking the gui during what is a pretty lengthy scraping operation, I am using QtReactor (as I saw mentioned in a couple of places) to allow my GUI to update during scraping.
Right now I just have a spinning progress bar (range of 0,0) and it updates during scraping.
I have an issue, however, that once scraping is completing my application is exiting of its own accord. It's definitely related to QtReactor as without the first two lines of code added below, it works fine (but blocks GUI).
What's causing this?
Thanks.
My Main:
from qtreactor import pyqt4reactor
pyqt4reactor.install()
import sys
from PyQt4 import QtGui
from Gui.DkMainWindow import DkMainWindow
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
form = DkMainWindow()
form.show()
sys.exit(app.exec_())

I have never used QtReactor, but all the examples I have seen do the install after creating the application:
import sys
from PyQt4 import QtGui
from Gui.DkMainWindow import DkMainWindow
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
from qtreactor import pyqt4reactor
pyqt4reactor.install()
form = DkMainWindow()
form.show()
sys.exit(app.exec_())
But if that makes no difference, you might also want to try:
app.setQuitOnLastWindowClosed(False)

Related

How to run PyQt5 GUIs in non-blocking threads?

I have a PyQt5 GUI class that I want to be able to create multiple instances of either from an interactive console or normal run. I need these GUIs to be non-blocking so that they can be used while subsequent code runs.
I've tried calling app.exec__() in separate threads for each GUI like this answer, but the program sometimes crashes as the comment on the answer warned it would:
Run pyQT GUI main app in seperate Thread
And now I'm trying to get the code below working which I made based on this answer:
Run Pyqt GUI main app as a separate, non-blocking process
But when I run it the windows pop and and immediately disappear
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
import time
class MainWindow(QtWidgets.QWidget):
def __init__(self):
# call super class constructor
super(MainWindow, self).__init__()
# build the objects one by one
layout = QtWidgets.QVBoxLayout(self)
self.pb_load = QtWidgets.QPushButton('Load')
self.pb_clear= QtWidgets.QPushButton('Clear')
self.edit = QtWidgets.QTextEdit()
layout.addWidget(self.edit)
layout.addWidget(self.pb_load)
layout.addWidget(self.pb_clear)
# connect the callbacks to the push-buttons
self.pb_load.clicked.connect(self.callback_pb_load)
self.pb_clear.clicked.connect(self.callback_pb_clear)
def callback_pb_load(self):
self.edit.append('hello world')
def callback_pb_clear(self):
self.edit.clear()
def show():
app = QtWidgets.QApplication.instance()
if not app:
app = QtWidgets.QApplication(sys.argv)
win = MainWindow()
win.show()
if __name__ == '__main__':
show()
show()
EDIT - I don't see how this question is a duplicate. The 'duplicate' questions are only slightly related and don't provide solutions to my problem at all.
I want to be able to create multiple instances of a GUI (MainWindow in my example) by calling the show() function from either an interactive session or script, and I want those windows to stay on my screen while subsequent code is running.
EDIT2 - When I run the code as a script I can do what I want by using multiprocessing, see this demo:
https://www.screencast.com/t/5WvJNVSLm9OR
However I still need help because I want it to also work in interactive Python console sessions, and multiprocessing does not work in that case.
It isn't necessary to use separate threads or processes for this. You just need a way to maintain a reference to each new window when importing the script in a python interactive session. A simple list can be used for this. It is only necessary to explictly start an event-loop when running the script from the command-line; in an interactive session, it will be handled automatically by PyQt.
Here is an implementation of this approach:
...
_cache = []
def show(title=''):
if QtWidgets.QApplication.instance() is None:
_cache.append(QtWidgets.QApplication(sys.argv))
win = MainWindow()
win.setWindowTitle(title)
win.setAttribute(QtCore.Qt.WA_DeleteOnClose)
win.destroyed.connect(lambda: _cache.remove(win))
_cache.append(win)
win.show()
if __name__ == '__main__':
show('Foo')
show('Bar')
sys.exit(QtWidgets.QApplication.instance().exec_())
This is a minor addendum to #ekhumoro's answer. I don't have enough reputation to only add a comment so I had to write this as an answer.
#ekhumoro's answer almost fully answers #Esostack's question, but doesn't work in the Ipython console. After many hours of searching for the answer to this question myself, I came across a comment from #titusjan in a three year old thread (here) also responding to a good answer from #ekhumoro.
The missing part to #ekhumoro's answer which results in the gui windows freezing for Ipython specifically is that Ipython should be set to use the qt gui at launch or once running.
To make this work with Ipython:
Launch Ipython with ipython --gui=qt5
In a running Ipython console run the magic command %gui qt5
To fix it from a Python script you can run this function
def fix_ipython():
from IPython import get_ipython
ipython = get_ipython()
if ipython is not None:
ipython.magic("gui qt5")

ValueError: signal only works in main thread in Flask using threading

I use flask, tkinter and threading modules.
I need to run a tkinter process and a flask process to run at the same time and I want to use threading for that.
So I run
t1 = t.Thread(target=lambda: root.wait_window(root))
t1.start()
to start the tkinter part, and then
if __name__ == '__main__':
app.run(host="0.0.0.0", port=25000, debug=True)
to start the flask part. But it keeps returning "ValueError: signal only works in main thread" when I run the flask part. I tried putting the flask part into the thread and the tkinter part into the main program, but it returns the same error.
How can I fix it?

PyQt5 GUI freeze caused by Windows focus-follows-mouse

When Windows focus-follows-mouse-without-raising-the-window is enabled by either of the two methods linked to below, I consistently get PyQt5 GUI 'freezes' where you have to type any character in the terminal that you ran python from in order to unfreeze the GUI; complete description and test case (Windows 10, Python 3.6.1, PyQt5) is here: pyqt5 click in terminal causes GUI freeze
To enable the focus-follows-mouse-without-raise behavior, try either of these - they both work in Windows 10:
downloadable program ('X-Mouse' though that name is used by other programs):
https://joelpurra.com/projects/X-Mouse_Controls/
registry hack description:
https://sinewalker.wordpress.com/2010/03/10/ms-windows-focus-follows-mouse-registry-hacks/
So - a few questions:
can anyone reproduce the issue? It seems 100% reproducible for me, but it would be great to hear the same from someone else.
is there a way to change the python code to detect-and-circumvent focus-follows-mouse, or just to be immune to it, i.e. maybe by ensuring the GUI application always takes focus back again when you - for example - click in a dialog or qmessagebox owned by the main GUI window, or by some other means? (Is the object hierarchy set up optimally, and if not, maybe this could all be resolved by correcting the ownership structure?)
The brute-force solution seems to work, though I'd like to leave this question open to see if someone knows of a more optimal solution; it took a fair amount of searching to figure out the right way; mainly by taking a look a the open-source code for X-Mouse. Basically, this method takes effect immediately, whereas the registry hack doesn't take effect until reboot.
New version of pyqt_freeze_testcase.py (the file from the referenced stackoverflow question); the changes are only additions, noted between lines of hash marks:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
####################### added begin:
import win32gui
import win32con
####################### added end
# import the UI file created with pyuic5
from minimal_ui import Ui_Dialog
class MyWindow(QDialog,Ui_Dialog):
def __init__(self,parent):
QDialog.__init__(self)
self.parent=parent
self.ui=Ui_Dialog()
self.ui.setupUi(self)
################################# added begin:
self.initialWindowTracking=False
try:
self.initialWindowTracking=win32gui.SystemParametersInfo(win32con.SPI_GETACTIVEWINDOWTRACKING)
except:
pass
if self.initialWindowTracking:
print("Window Tracking was initially enabled. Disabling it for now; will re-enable on exit.")
win32gui.SystemParametersInfo(win32con.SPI_SETACTIVEWINDOWTRACKING,False)
################################# added end
def showMsg(self):
self.really1=QMessageBox(QMessageBox.Warning,"Really?","Really do stuff?",
QMessageBox.Yes|QMessageBox.No,self,Qt.WindowTitleHint|Qt.WindowCloseButtonHint|Qt.Dialog|Qt.MSWindowsFixedSizeDialogHint|Qt.WindowStaysOnTopHint)
self.really1.show()
self.really1.raise_()
if self.really1.exec_()==QMessageBox.No:
print("nope")
return
print("yep")
################################## added begin:
def closeEvent(self,event):
if self.initialWindowTracking:
print("restoring initial window tracking behavior ("+str(self.initialWindowTracking)+")")
win32gui.SystemParametersInfo(win32con.SPI_SETACTIVEWINDOWTRACKING,self.initialWindowTracking)
################################## added end
def main():
app = QApplication(sys.argv)
w = MyWindow(app)
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Implementing web inspection in browser using PyQt5

QtWebKit is no longer supported in PyQt5.
Although there are alternates to some of the classes of QtWebKit in QtWebEngineWidgets. But, i couldn't find any alternate to the QWebInspector class that is available in PyQt4.
Are there any such classes OR even any other option so that i can implement web inspector using PyQt5 ?
Edit: Qt5.6 and beyond has removed QtWebKitWidgets
I was somewhat surprised to find that QtWebKit is making a comeback. It is still not part of Qt-5.6 or Qt-5.7, but it seems that it may continue to be maintained as separate project. This means PyQt5 can continue to support QtWebKit, even though the official Qt5 docs say it has been removed.
Depending on your platform, this probably means you will need to install some extra packages if you want to use the "new" QtWebKit module in PyQt5.
PS:
As for QtWebEngine - if you're using ubuntu/debian, it seems you will have to wait for it to be supported. See Bug #1579265.
I show the following example to use QWebInspector in PyQt5 version 5.7.1
from PyQt5.QtCore import QUrl
from PyQt5.QtWebKit import QWebSettings
from PyQt5.QtWebKitWidgets import QWebView, QWebInspector
from PyQt5.QtWidgets import QApplication, QSplitter, QVBoxLayout, QWidget
class Window(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent=parent)
self.view = QWebView(self)
self.view.settings().setAttribute(
QWebSettings.DeveloperExtrasEnabled, True)
self.inspector = QWebInspector()
self.inspector.setPage(self.view.page())
self.inspector.show()
self.splitter = QSplitter(self)
self.splitter.addWidget(self.view)
self.splitter.addWidget(self.inspector)
layout = QVBoxLayout(self)
layout.addWidget(self.splitter)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.view.load(QUrl('http://www.google.com'))
window.show()
sys.exit(app.exec_())

QLineEdit.setText only works once in function

I'm currently trying to program for my bachelor thesis. The main part works, so now I want to implement a user interface. I watched some tutorials and worked via trial and error, and my user interface also works. So far so good. But yesterday I changed a small thing and that doesn't do what i want. I have a button saying "start program", and a line-edit where i want to display the current status. My code is:
import sys
from PyQt4 import QtGui
from theguifile import Ui_MainWindow
import otherfile
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui=Ui_MainWindow()
self.ui.setupUi(self)
self.ui.mybutton.clicked.connect(self.runprogram)
def runprogram(self):
self.ui.mylineedit.setText('Running') # doesnt work
try:
otherfile.therealfunction() # works
except ErrorIwanttocatch:
self.ui.mylineedit.setText('thisErrorhappened') # works
else:
self.ui.mylineedit.setText('Success') # works
app = QtGui.QApplication(sys.argv)
window = Main()
sys.exit(app.exec_())
Everything works as I want except from lineedit.setText('Running'). What I want is that "Running" is displayed while otherfile.therealfunction is working. I guess I have to update the line-edit somehow? But until now I didn't figure out how I can do that. I also set the line-edit readonly because I don't want the user to be able to change it, so maybe that's a problem? I thought readonly would only affect what the user can do.
I am using Python3 and PyQt4 with Qt Designer.
Calling otherfile.therealfunction() will block all ui updates until the function completes. You can try forcing immediate ui updates like this:
def runprogram(self):
self.ui.mylineedit.setText('Running')
QtGui.qApp.processEvents()

Resources