self.exit and self.quit do not exit PyQt application - python

I'm trying to write a PyQt application that renders HTML, waits, and finishes. I've got the rendering mostly down (but stripped out here for simplicity's sake). The code here runs and hangs, it seems as if the self.exit call (or self.quit) is not being honored. Having no prior experience with Qt, I feel as if I'm missing something obvious. Can someone shed some light on this?
I've also tried putting the exit calls at the end of the render function.
from PyQt4.Qt import QApplication
from PyQt4.QtWebKit import QWebView
import sys
class ScreenshotterApplication(QApplication):
def __init__(self, args, html):
QApplication.__init__(self, args)
self.browser = QWebView()
self.render(html)
self.exit()
def render(self, html):
print html
if __name__ == "__main__":
app = ScreenshotterApplication(sys.argv, '<h1>Hello, World!</h1>')
sys.exit(app.exec_())

I don't really understand what you are trying to do but, that said, you are calling exit (or quit) in the constructor of your QApplication. exit (or quit) tells the QApplication to leave its event loop, but in the constructor, the eventloop is not yet started (it is once exec_ is called).

Related

PyQt5: QApplication unexpectedly exits when QMessageBox is called as a menu action from QSystemTrayIcon

The code below is a simplified version of the code I am writing. The purpose of this PyQt5 code is to display an icon in the system tray while some system maintenance process is running, and to give the user an opportunity to terminate the maintenance process before it finishes normally, which for example would be the case when the process takes too long to complete.
The code below demonstrates the problem I have encountered. MenuItem2, when invoked, creates a QMessageBox asking user to click "ok" or "cancel". When user's response is received, the whole QApplication terminates. I have no idea why this happens. I added time.sleep(2) just to demonstrate that QMessageBox works just fine and the "if" block after QMessageBox.exec() executes normally with QApplication working normally, but as soon as the "if" block is executed and dialog_ok_cancel() function finishes the whole QApplication terminates which is evidenced by the disappearance of the system tray icon immediately after printing messages within the "if" block.
On the contrary, MenuItem1 works just fine and QApplication continues to run. Here is the code:
import sys, time
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import QSystemTrayIcon, QApplication, QMenu, QMessageBox
def menuitem1_action():
print('MenuItem1 chosen')
def dialog_ok_cancel():
msgBox = QMessageBox()
msgBox.setIcon(QMessageBox.Warning)
msgBox.setText('Do you really want to terminate this process?')
msgBox.setWindowTitle('Confirm your choice')
msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
returnValue = msgBox.exec()
time.sleep(2)
if returnValue == QMessageBox.Ok:
print('OK clicked')
else:
print('Cancel clicked')
class SystemTrayIcon(QSystemTrayIcon):
def __init__(self, icon):
QSystemTrayIcon.__init__(self, icon)
menu = QMenu()
item1 = menu.addAction('MenuItem1')
item1.triggered.connect(menuitem1_action)
item2 = menu.addAction('MenuItem2')
item2.triggered.connect(dialog_ok_cancel)
self.setContextMenu(menu)
app = QApplication(sys.argv)
trayIcon = SystemTrayIcon(QtGui.QIcon('gtk-save.png'))
trayIcon.show()
app.exec()
I don't mind the termination of QApplication when the user clicks on "Ok". The problem is that the application terminates when the user clicks on "Cancel" despite the fact that there is nothing in the code above causing QApplication to quit.
Found the answer. From the official documentation of the "QApplication" class:
The application quits when the last window was successfully closed;
this can be turned off by setting quitOnLastWindowClosed to false.
Apparently, QSystemTrayIcon does not qualify as a window. The dialog window that opens up on an invocation of MenuItem2 is then the only window, and when it is closed on user's clicking either "Ok" or "Cancel", the whole QApplication exits.
Adding
app.setQuitOnLastWindowClosed(False)
immediately below "app = QApplication(sys.argv)" solves the problem.

PyQt QThread cause main thread to close

Hello i have a problem with creating Thread using the QThread class.
here is my code :
import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import QThread
class ScriptRunner(QThread):
def run(self):
print('test')
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = QWidget()
gui.setFixedSize(400, 400)
gui.setVisible(True)
ScriptRunner().start() # this line cause the error
sys.exit(app.exec_())
when i lunch this code without the ScriptRunner().start() line, the gui is working without any problem, but when i do add the line, the gui show up and is hidden very quickly and program is shutdown
I receive this message in the console :
/usr/bin/python3.6 /home/karim/upwork/python-qt-rasp/tests/test.py
QThread: Destroyed while thread is still running
Process finished with exit code 134 (interrupted by signal 6: SIGABRT)
Please change the line:
ScriptRunner().start() # this line cause the error
to:
sr = ScriptRunner()
sr.start()
Tested for PyQt4 though, but works.
EDIT:
Another workaround that worked for me:
Instantiating ScriptRunner as ScriptRunner(gui).start() also works.
The problem was that you must save a reference to the thread that you lunch, in my real world example, i was not saving a reference to the thread in my object, so its was garbage collected by python i think.
self.runner = Runner()

How to create PyQt5 GUI that does not occupy the main thread

Is it possible to get Python to launch a PyQt5 GUI application using the main thread of execution, and then leave the thread open to do other things?
Here is the simplest example I can think of:
from PyQt5.QtWidgets import *
def infinite_useless_loop():
while True:
pass
app = QApplication([])
doodad = QMainWindow()
doodad.show()
infinite_useless_loop()
If you run this code, it freezes because the 'mainloop' is not activated as it normally would be if I didn't make a call to the useless infinite loop, and instead put a call to app.exec_(). The call to infinite_useless_loop is meant to substitute the main thread being used to do other useful work WHILE the GUI is running correctly.
To do this correctly, I'd have to imagine one would make a separate thread and use that to run the GUI, while launching that thread with the main thread, and just continuing on from there; able to communicate if necessary in the future.
EDIT
I wrote some code that gets the main event loop working in a seperate thread, but I am still not able to fully interact with the GUI object that is created in another thread. This code effectively solves half of my problem:
from PyQt5.QtWidgets import *
from threading import Thread
import sys
def makeGUI():
app = QApplication([])
doodad = QMainWindow()
doodad.show()
sys.exit(app.exec_())
def infinite_useless_loop():
while True:
pass
if __name__ == '__main__':
thread = Thread(target=makeGUI)
thread.start()
infinite_useless_loop()
This code spawns the blank window, able to be clicked on and resized correctly (because the event loop is working), but is on its own. Do I have to revert to signals and slots to communicate with the object, or can I somehow get the GUI object back into the main thread and use it's methods from there?
EDIT 2
I do not wish to do any actual GUI manipulation outside of the GUI's thread, just call methods on the GUI object that I HAVE MADE. The entire GUI structure would be made by the GUI object.
EDIT 3
This new code now makes it able to be communicated with, but only with raw bits of information, and not by one thread accessing another thread's GUI object's methods. This is close, but its still not what I want. Calling the GUI object's methods would have to be done by passing the raw information through the queue, and then being processed at the other side. Then an exec() function would have to be called. That is not acceptable. Here is the code:
from PyQt5.QtWidgets import *
import multiprocessing
import threading
import sys
import time
import queue
class Application(QMainWindow):
def __init__(self, comms):
super(Application, self).__init__()
self.comms = comms
self.fetchingThread = threading.Thread(target=self.fetchingLoop)
self.labelOne = QLabel(self)
self.layout = QVBoxLayout()
self.layout.addWidget(self.labelOne)
self.setLayout(self.layout)
self.fetchingThread.start()
def fetchingLoop(self):
while True:
try:
obj = self.comms.get()
self.processItem(obj)
except queue.Empty:
pass
def processItem(self, item):
self.labelOne.setText(str(item))
print(self.labelOne.text())
class Launcher(multiprocessing.Process):
def __init__(self, Application):
super(Launcher, self).__init__()
self.comms = multiprocessing.Queue()
self.Application = Application
def get_comms(self):
return self.comms
def run(self):
app = QApplication([])
window = self.Application(self.comms)
window.show()
sys.exit(app.exec_())
def no_thread():
app = QApplication([])
window = Application(False)
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
thread = Launcher(Application)
comms = thread.get_comms()
thread.start()
c = 0
while True:
c += 1
time.sleep(1)
comms.put(c)

Pyqt Terminal hangs after excuting close window command

I have read lots of threads online, but still I could not find the solution. My question should be very simple: how to close a Pyqt window WITHOUT clicking a button or using a timer.
The code I tried is pasted below
from PyQt4 import QtGui, QtCore
import numpy as np
import progressMeter_simple
import sys
import time
import pdb
class ProgressMeter(progressMeter_simple.Ui_Dialog, QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
progressMeter_simple.Ui_Dialog.__init__(self)
self.setupUi(self)
self.progressBar.setRange(0, 0)
QtGui.QApplication.processEvents()
def termination(self):
time.sleep(5)
self.close()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
Dialog = ProgressMeter()
Dialog.show()
Dialog.termination()
sys.exit(app.exec_())
My Pyqt GUI is designed using Qt designer, and it is nothing but a progress bar that keeps moving from left to right (busy indication).
However, when I run the code above, the terminal still hangs after the Pyqt window is closed. Ctrl+C also couldn't kill the process.
In short, how can I properly close/terminate a Pyqt window without clicking a button or using a timer?
It's not working because you're calling GUI methods on the dialog (close()) outside of the event loop. The event loop doesn't start until you call app.exec_().
If you really want to close the dialog immediately after it opens without using a QTimer, you can override the showEvent() method and call termination() from there, which gets called when the dialog is first displayed.
class ProgressMeter(progressMeter_simple.Ui_Dialog, QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
progressMeter_simple.Ui_Dialog.__init__(self)
self.setupUi(self)
self.progressBar.setRange(0, 0)
def showEvent(self, event):
super(ProgressMeter, self).showEvent(event)
self.termination()

How to Restart PyQt4 Application

Is there a way to restart PyQt application QApplication
I have an app created with pyqt4 and python 2.6 using below code
app = QtGui.QApplication(sys.argv)
i have settings options where i set some settings. Now when i save settings i need to reload the application so that new settings are effected. Without the need of end user to exit and launch the app again.
I had a similar problem and simply used this at the appropriate place:
subprocess.Popen([__file__])
sys.exit(0)
It was a simple application, and didn't need any further arguments.
I explain how I did it :
I create a extra one file main.py which calls my actual main program file dash.py.
And I emits a signal for restarting (my programs auto updates at the closeEvent) so I required to emit a signal for it. This is the snippets hope this will help you.
This one is in my main program file in dash.py
def restart(self):
# create a signal equivalent to "void someSignal(int, QWidget)"
self.emit(QtCore.SIGNAL("RESTARTREQUIRED"), True)
This one in main.py which calls actual program only and restarts the app
import sys
from PyQt4 import QtGui,QtCore
from bin import dash
if __name__ == "__main__":
application = QtGui.QApplication(sys.argv)
uDesk = dash.app()
uDesk.show()
uDesk.actionRestart.triggered.disconnect()
# define restart slot
#QtCore.pyqtSlot()
def restartSlot():
print 'Restarting app'
global uDesk
uDesk.deleteLater()
uDesk = dash.app()
uDesk.show()
uDesk.actionRestart.triggered.disconnect()
uDesk.actionRestart.triggered.connect(restartSlot)
print 'New app started !'
QtCore.QObject.connect(uDesk,
QtCore.SIGNAL("RESTARTREQUIRED"),
restartSlot)
uDesk.actionRestart.triggered.connect(restartSlot)
sys.exit(application.exec_())
Hope this was helpful !!
EDIT: Changing the way to get the application path
You could just start a new process and exit yours, something like this: (CODE NOT TESTED, but based on this answer)
// Restart Application
def restart(self, abort):
// Spawn a new instance of myApplication:
proc = QProcess()
//proc.start(self.applicationFilePath());
import os
proc.start(os.path.abspath(__file__))
self.exit(0);
Code it as a method of your Qapplication or even a function if you don't feel like subclassing
This is how I restart TicTacToe game in PySide (it should be the same in PyQt):
I have a single class - a QWidget class - in which is coded the Tic Tac Toe game. To restart the application I use:
import subprocess
a QPushButton() like so:
self.button = QPushButton("Restart", self)
the connection of the button to Slot:
self.buton.clicked.connect(self.restartGame)
the Slot for this button, like so:
def restartGame(self):
self.close()
subprocess.call("python" + " TicTAcToe.py", shell=True)
All these are in the same - single - class. And what these do: close the active window of the game and create a new one.
How this code looks in the TicTacToe class:
import subprocess
class TicTacToe(QWidget):
def __init__(self):
QWidget.__init__(self)
self.button = QPushButton("Restart", self)
self.buton.clicked.connect(self.restartGame)
def restartGame(self):
self.close()
subprocess.call("python" + " TicTacToe.py", shell=True)
def main():
app = QApplication(sys.argv)
widget = TicTacToe()
widget.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
EDIT
I know this doesn't answer the question (it doesn't restart a QApplication), but I hope this helps those who want to restart their QWidget single class.

Categories

Resources