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.
Related
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.
Is it possible to set up a Python script with a PyQt5 GUI such that an active terminal can interact with it?
An example project:
from PyQt5 import QtWidgets from PyQt5.QtCore import pyqtSlot
class Machine:
"""Simpel state machine"""
STATE_IDLE = 'idle'
STATE_OPERATION = 'operation'
def __init__(self):
self.state = self.STATE_IDLE
def get_state(self):
return self.state
def set_state(self, new_state):
self.state = new_state
#pyqtSlot() def on_click():
"""Callback for the PyQt button"""
print('State:', machine.get_state())
if __name__ == '__main__':
# Create machine instance
machine = Machine()
# Set up GUI
app = QtWidgets.QApplication([])
window = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
label = QtWidgets.QLabel('Hello World!')
button = QtWidgets.QPushButton('Go')
button.clicked.connect(on_click)
layout.addWidget(label)
layout.addWidget(button)
window.setLayout(layout)
window.show()
code = app.exec()
exit(code)
I would like to run this script and to then be able to access the objects from the same terminal. Something like:
$ python main.py
State: idle [< I just pressed the GUI button]
>> machine.set_state(Machine.STATE_OPERATION)
State: operation [< I just pressed the GUI button again]
If I run it like:
python -i main.py
The command line hangs until the application was closed (that's what .exec() does after all), but then I can access machine. Can I make .exec() not hang like this? Maybe run app from a separate thread?
EDIT: I found that thy Pyzo IDE allows you to create a shell with PyQt5 support, which handles the PyQt event loop. So when using the script above with such a Pyzo shell, I can remove code = app.exec() exit(code) because Pyzo will run the event loop and keep the window alive. Now I'd like to do the same across all IDEs.
I guess that I am not closing my PyQT5 Window correctly. I am using spyder (3.3.5) which I have installed with anaconda, to program a pyqt5 program. I am using qt creator to design my ui file, which I load using the loadUi function in pyqt package. Everything works fine with the code, until I need to close it. I close the window via the close button (the x button in the top right). The window itself is closed, but it seems like the console (or shell) is stuck, and I can't give it further commands or to re run the program, without having to restart the kernel (to completely close my IDE and re opening it).
I have tried looking for solutions to the problem in the internet, but none seems to work for me. Including changing the IPython Console > Graphics to automatic.
Edit: Also Created an issure:
https://github.com/spyder-ide/spyder/issues/9991
import sys
from PyQt5 import QtWidgets,uic
from PyQt5.QtWidgets import QMainWindow
class Mic_Analysis(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.ui=uic.loadUi("qt_test.ui",self)
...
if __name__ == "__main__":
def run_app():
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
app=QtWidgets.QApplication.instance()
mainWin=Mic_Analysis()
mainWin.show()
app.exec_()
run_app()
If someone have any suggestion I would be very happy to hear them.
For me, it helped to remove the 'app.exec_()' command.
But then it closes immediatly when running the code. To keep the window open, I needed to return the MainWindow instance to the global scope (or make it a global object). My code looks like this:
from PyQt5 import QtWidgets
import sys
def main():
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
app = QtWidgets.QApplication.instance()
main = MainWindow()
main.show()
return main
if __name__ == '__main__':
m = main()
Try adding :
app.setQuitOnLastWindowClosed(True)
to your main() function
def main():
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(True)
win = MainWindow()
win.show()
sys.exit(app.exec_())
Surprizinglly, for me this works well. No QApplication is needed.
It seems to work in another thread.
from PyQt5 import QtWidgets,uic
class Ui(QtWidgets.QDialog):
def __init__(self):
super().__init__()
uic.loadUi('./test.ui',self)
self.show()
w=Ui()
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)
Ok I hope I can make this understandable...
So I created an UI in QT designer - named heatwindow.ui
Next I wrote this following script in a file named heatwindow.py:
from PySide import QtGui, QtCore, QtUiTools
from HeatModel import *
from vsmutils import *
class HeatWindow:
"""Main window class for the Flow application"""
def __init__(self, app):
"""Class constructor"""
# Assign our application instance as a member variable
self.app = app
# Create an instance of our HeatModel class
#self.heatModel = HeatModel()
# Load and show our user interface
self.ui = loadUiWidget('heatwindow.ui')
self.ui.show()
self.ui.raise_()
if __name__ == '__main__':
app = appInstance()
app.Create()
HeatWindow(app)
window = HeatWindow(app)
app.Run()
Ok, so vsmutils is a script that we got from our teacher...
Anyways - my problem is that nothing happens when running the script. Actually it seems the process in Python gets killed entirely, I have to press "Run again this program" (Spyder) in the Console. When debugging it seems this happens after the line
window = HeatWindow(app)
I hope this is enough info to help me, if not please tell what I should add!
Regards
Ok, solved it finally.
So I am going to post how I did it for others to see if they had the same problem... Cause this was just frustrating.
So- I use Windows 7, Python 2.7.6, Spyder... The script crashed all the time when using QT/Pyside. The solution was to delete a file qt.conf that is inside a Python 2.7.6 folder... And now it works... Don't know why though, but it took me 16 hours to find out.