PyQt QThread cause main thread to close - python

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()

Related

How can I make my PyQt5 app oney one instance?

What I want to achieve:
when I run application from start menu, app starts (if app not running).
If an app is already running, then don't create another instance, just show the previous running app window.
What I've tried:
created a .txt file in a directory, write 'running' & 'not running' into the file while opening & exiting the window. And checking the file contents at very start. For ex:
When starting app: 1.) Check the file content, 2.) If file content is 'running', show warning & exit the app 3.) If file content is 'not running', write 'running' into the file & start app 4.) Write 'not running' into the file on exit.
Issues I'm facing:
This method doesn't feel like the right way to achieve this.
If file item says 'not running', app shows warning and exits. While I want it to show already running instance.
File items not updated to 'not running' when app exits because of some error in code (because that part is never reached due to error)
Can anybody please help me out regarding this?
You can achieve something similar with the win32gui module. To install it type into CMD pip install pywin32. Now this is the code:
from win32 import win32gui
import sys
def windowEnumerationHandler(hwnd, top_windows):
top_windows.append((hwnd, win32gui.GetWindowText(hwnd)))
top_windows = []
win32gui.EnumWindows(windowEnumerationHandler, top_windows)
for i in top_windows:
if "Program" in i[1]: #CHANGE PROGRAM TO THE NAME OF YOUR WINDOW
win32gui.ShowWindow(i[0],5)
win32gui.SetForegroundWindow(i[0])
sys.exit()
from PyQt5.QtWidgets import QApplication, QWidget
def main():
#YOUR PROGRAM GOES HERE
app = QApplication(sys.argv)
w = QWidget()
w.setGeometry(500, 500, 500, 500)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Basically, at the beginning, the program gets the name of every open window. If the window name is equal to the name of the program, then it brings the program to the front and closes the program. If not, then it opens a new program.
PyWin32 link
Stack Overflow get a list of every open window
This solution doesn't require you to install win32 or any other module, works with PyQt5 package itself.
import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSystemSemaphore, QSharedMemory
from ui import window_ui # .py file compiled from .ui file
class LoginWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.ui = window_ui.Ui_MainWindow() # call to init ui
self.ui.setupUi(self)
def launch():
app = QtWidgets.QApplication(sys.argv) # create app instance at top, to able to show QMessageBox is required
window_id = 'pingidapplication'
shared_mem_id = 'pingidsharedmem'
semaphore = QSystemSemaphore(window_id, 1)
semaphore.acquire() # Raise the semaphore, barring other instances to work with shared memory
if sys.platform != 'win32':
# in linux / unix shared memory is not freed when the application terminates abnormally,
# so you need to get rid of the garbage
nix_fix_shared_mem = QSharedMemory(shared_mem_id)
if nix_fix_shared_mem.attach():
nix_fix_shared_mem.detach()
shared_memory = QSharedMemory(shared_mem_id)
if shared_memory.attach(): # attach a copy of the shared memory, if successful, the application is already running
is_running = True
else:
shared_memory.create(1) # allocate a shared memory block of 1 byte
is_running = False
semaphore.release()
if is_running: # if the application is already running, show the warning message
QtWidgets.QMessageBox.warning(None, 'Application already running',
'One instance of the application is already running.')
return
# normal process of creating & launching MainWindow
window = LoginWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
launch()
This solution I converted from a C++ code posted on this website originally.

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.

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)

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.

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

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).

Categories

Resources