QT4Reactor / Scrapy / PyQt4 - pyqt4

How to use Qt4Reactor the right way?
I have a PyQt4 Application running Scrapy to parse a website and show the results in the ui.
When the user clicks a button, the scraper gets started in another process. The ui blocks and the scraper is running. When the scraper is finished, the data is shown in the ui. What I want is a non-blockung ui when Scrapy is running.
Since Scrapy is build on top of twisted, I have to use a new process instead of a thread to start it from the ui.
My question is how to achieve a non-blocking ui as simple as possible?
I tried to use Qt4Reactor like this:
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
import qt4reactor
qt4reactor.install()
MainWindow = DiscogsRenamer()
MainWindow.show()
sys.exit(app.exec_())
reactor.runReturn()
This results in an error:
Traceback (most recent call last):
File "/home/f/work/py/discogs_renamer/main.py", line 224, in <module>
qt4reactor.install()
File "/usr/local/lib/python2.7/dist-packages/qt4reactor.py", line 338, in posixinstall
installReactor(p)
File "/usr/local/lib/python2.7/dist-packages/twisted/internet/main.py", line 32, in installReactor
raise error.ReactorAlreadyInstalledError("reactor already installed")
twisted.internet.error.ReactorAlreadyInstalledError: reactor already installed
I posted another question to this topic:
Integrate Scrapy/Twisted event loop in PyQt4 Main loop
Thanks in advance.

You must import and install() the qt4reactor before importing twisted.internet.reactor.
import qt4reactor
qt4reactor.install()
from twisted.internet import reactor

Related

Missing module attribute showing up for a linked Python script

In my learning to use Python one of the things I am trying to do is to separate like functions/details in to different scripts. In a previous script that I wrote that was a batch file renamer, I have created a GUI and I wanted to separate the GUI and its specific functions into a dedicated script that the parent script can call upon at initial opening.
The parent script is called TVShowRenamerv2.py
The GUI script is called GUI.py
From the parent script, this is imported
import tkinter as tk
from tkinter import simpledialog, filedialog, messagebox, ttk
import GUI as g
And then called on a line near the end of the script
rg = g.Renamer_GUI()
I am using VS Code as my IDE, and I get no errors prior to running the script. Once I do run the script, I get the following error:
File "TVShowRenamerv2.py", line 4, in <module>
import GUI as g
File "D:\Projects\Python\TVShowRenamer\v2\GUI.py", line 3, in <module>
import TVShowRenamerv2 as t2
File "D:\Projects\Python\TVShowRenamer\v2\TVShowRenamerv2.py", line 172, in <module>
rg = g.Renamer_GUI()
AttributeError: module 'GUI' has no attribute 'Renamer_GUI'
This is from the GUI.py
from tkinter import ttk
import TVShowRenamerv2 as t2
class Renamer_GUI():
VS Code seems to tie the two together properly because I don't get any errors when I call functions from the two scripts, so I am not sure what the issue is. Any help is greatly appreciated. Thank you.
As it turns out, I was implementing a circular import of the two different scripts (ie, both scripts were importing each other). As a more experienced Python dev friend pointed out to me, this is bad.
We initially solved the problem by placing the items that the debugger was stating as missed attributes in one of the scripts inside a name == main if statement, but that was a short term fix that ultimately ended up creating additional issues.
So I ended up creating a third script (A controller, a GUI, and now a manipulable values). The controller would create the TK window and instantiate the GUI class inside it, and then the GUI could manipulate the values in the third script. This seemed to be the better long term solution.
Thank you very much for the assistance!

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?

QtReactor Closes Application Window (Using Scrapy)

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)

Certain Tkinter options not available when in IDLE

I am trying to create a button that when the widget is active in changes color (I am using python-idle3).
I tried importing tkinter as follows:
from tkinter import *
from tkinter import ttk
My button creation codes are as follows but does not work when I included the activebackground option
start_button=Button(window_frame,text="START",width=70,height=2).grid(row=8,column=0,stick="E",activebackground="black")
Error message when using activebackground:
Traceback (most recent call last): File "/root/Desktop/GUi #1.py",
line 37, in <module>
Button_expirement() File "/root/Desktop/GUi #1.py", line 26, in Button_expirement
start_button=Button(window_frame,text="START",width=70,height=2).grid(row=8,column=0,stick="E",activebackground="black")
File "/usr/lib/python3.2/tkinter/__init__.py", line 1914, in
grid_configure
+ self._options(cnf, kw))
**_tkinter.TclError: bad option "-activebackground": must be -column, -columnspan, -in, -ipadx, -ipady, -padx, -pady, -row, -rowspan, or -sticky**
I noticed that some other widget features are also limited. Any solution to this?
activebackground isn't part of the function call to grid. See http://effbot.org/tkinterbook/grid.htm
But it is in the function call to Button. See http://effbot.org/tkinterbook/button.htm
So perhaps you meant to write this?
start_button=Button(window_frame,text="START",width=70,height=2,activebackground="black").grid(row=8,column=0,stick="E")
...or if you meant to set the background to black, you can set it in the Tk variable (see
Python Tkinter how to color a window using grid).
root = Tk()
root.configure(bg='black')

Resources